Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

616 lines
17 KiB

  1. /****************************************************************************
  2. *
  3. * wdmaud.c
  4. *
  5. * WDM Audio mapper
  6. *
  7. * Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
  8. *
  9. * History
  10. * 5-12-97 - Noel Cross (NoelC)
  11. *
  12. ***************************************************************************/
  13. #include <stdarg.h>
  14. #include "wdmdrv.h"
  15. LPDEVICEINFO pWaveDeviceList = NULL;
  16. LPDEVICEINFO pMidiDeviceList = NULL;
  17. #ifdef DEBUG
  18. INT giAllocs=0;
  19. INT giFrees=0;
  20. #endif
  21. //--------------------------------------------------------------------------
  22. // LPDEVICEINFO GlobalAllocDeviceInfo
  23. //
  24. // Note: when allocating DeviceInfo structure, we know that the structure's
  25. // definition includes one character for the DeviceInterface, so we only need
  26. // to allocate additional length for the string but not its NULL terminator
  27. //--------------------------------------------------------------------------
  28. LPDEVICEINFO GlobalAllocDeviceInfo(LPCWSTR DeviceInterface)
  29. {
  30. LPDEVICEINFO DeviceInfo;
  31. IsValidDeviceInterface(DeviceInterface);
  32. DeviceInfo = GlobalAllocPtr(GPTR, sizeof(*DeviceInfo)+(sizeof(WCHAR)*lstrlenW(DeviceInterface)));
  33. if (DeviceInfo) {
  34. lstrcpyW(DeviceInfo->wstrDeviceInterface, DeviceInterface);
  35. #ifdef DEBUG
  36. DeviceInfo->dwSig=DEVICEINFO_SIGNATURE;
  37. #endif
  38. }
  39. DPF(DL_TRACE|FA_ALL,("Allocated DI=%08X, giAllocs=%d, giFrees=%d",
  40. DeviceInfo,++giAllocs,giFrees) );
  41. return DeviceInfo;
  42. }
  43. VOID GlobalFreeDeviceInfo(LPDEVICEINFO lpdi)
  44. {
  45. //
  46. // Now free the deviceinfo structure.
  47. //
  48. if( lpdi )
  49. {
  50. #ifdef DEBUG
  51. giFrees++;
  52. // remove the signature from the block.
  53. lpdi->dwSig=0;
  54. #endif
  55. GlobalFreePtr( lpdi );
  56. }
  57. }
  58. /****************************************************************************
  59. * @doc INTERNAL
  60. *
  61. * @api MMRESULT | wdmaudOpenDev | Open the kernel driver device corresponding
  62. * to a logical wave device id
  63. *
  64. * @parm UINT | DeviceType | The type of device
  65. *
  66. * @parm DWORD | dwId | The device id
  67. *
  68. * @comm For our sound devices the only relevant access are read and
  69. * read/write. Device should ALWAYS allow opens for read unless some
  70. * resource or access-rights restriction occurs.
  71. ***************************************************************************/
  72. MMRESULT wdmaudOpenDev
  73. (
  74. LPDEVICEINFO DeviceInfo,
  75. LPWAVEFORMATEX lpWaveFormat
  76. )
  77. {
  78. MMRESULT mmr;
  79. UINT cbSize;
  80. DPFASSERT(DeviceInfo->DeviceType == WaveOutDevice ||
  81. DeviceInfo->DeviceType == WaveInDevice ||
  82. DeviceInfo->DeviceType == MidiOutDevice ||
  83. DeviceInfo->DeviceType == MidiInDevice ||
  84. DeviceInfo->DeviceType == MixerDevice);
  85. //
  86. // Check it's not out of range
  87. //
  88. if (DeviceInfo->DeviceNumber > WDMAUD_MAX_DEVICES)
  89. {
  90. MMRRETURN( MMSYSERR_BADDEVICEID );
  91. }
  92. if (NULL != lpWaveFormat)
  93. {
  94. if (WAVE_FORMAT_PCM == lpWaveFormat->wFormatTag)
  95. {
  96. cbSize = sizeof(PCMWAVEFORMAT);
  97. }
  98. else
  99. {
  100. //
  101. // because MMSYSTEM does not (currently) validate for the extended
  102. // format information, we validate this pointer
  103. //
  104. cbSize = sizeof(WAVEFORMATEX) + lpWaveFormat->cbSize;
  105. if (IsBadReadPtr(lpWaveFormat, cbSize))
  106. {
  107. MMRRETURN( MMSYSERR_INVALPARAM );
  108. }
  109. }
  110. //
  111. // Store this for positional information
  112. //
  113. DeviceInfo->DeviceState->cSampleBits = lpWaveFormat->nChannels * lpWaveFormat->wBitsPerSample;
  114. }
  115. else
  116. {
  117. cbSize = 0L;
  118. }
  119. mmr = wdmaudIoControl(DeviceInfo,
  120. cbSize,
  121. lpWaveFormat,
  122. IOCTL_WDMAUD_OPEN_PIN);
  123. //
  124. // Return status to caller
  125. //
  126. MMRRETURN( mmr );
  127. }
  128. /****************************************************************************
  129. * @doc INTERNAL
  130. *
  131. * @api MMRESULT | wdmaudCloseDev | Close the kernel driver device corresponding
  132. * to a logical device id
  133. *
  134. * @parm UINT | DeviceType | The type of device
  135. *
  136. * @parm DWORD | dwId | The device id
  137. *
  138. * @comm For our sound devices the only relevant access are read and
  139. * read/write. Device should ALWAYS allow opens for read unless some
  140. * resource or access-rights restriction occurs.
  141. ***************************************************************************/
  142. MMRESULT FAR wdmaudCloseDev
  143. (
  144. LPDEVICEINFO DeviceInfo
  145. )
  146. {
  147. MMRESULT mmr;
  148. DPFASSERT(DeviceInfo->DeviceType == WaveOutDevice ||
  149. DeviceInfo->DeviceType == WaveInDevice ||
  150. DeviceInfo->DeviceType == MidiOutDevice ||
  151. DeviceInfo->DeviceType == MidiInDevice ||
  152. DeviceInfo->DeviceType == MixerDevice);
  153. //
  154. // Check it's not out of range
  155. //
  156. if (DeviceInfo->DeviceNumber > WDMAUD_MAX_DEVICES)
  157. {
  158. MMRRETURN( MMSYSERR_BADDEVICEID );
  159. }
  160. if (WaveOutDevice == DeviceInfo->DeviceType ||
  161. WaveInDevice == DeviceInfo->DeviceType)
  162. {
  163. if (DeviceInfo->DeviceState->lpWaveQueue)
  164. {
  165. return WAVERR_STILLPLAYING;
  166. }
  167. //
  168. // Wait for the thread to be destroyed.
  169. //
  170. mmr = wdmaudDestroyCompletionThread(DeviceInfo);
  171. if (MMSYSERR_NOERROR != mmr)
  172. {
  173. MMRRETURN( mmr );
  174. }
  175. }
  176. else if (MidiInDevice == DeviceInfo->DeviceType)
  177. {
  178. if (DeviceInfo->DeviceState->lpMidiInQueue)
  179. {
  180. DPF(DL_WARNING|FA_MIDI,("Error closing midi device") );
  181. return MIDIERR_STILLPLAYING;
  182. }
  183. InterlockedExchange( (LPLONG)&DeviceInfo->DeviceState->fExit, TRUE );
  184. }
  185. mmr = wdmaudIoControl(DeviceInfo,
  186. 0,
  187. NULL,
  188. IOCTL_WDMAUD_CLOSE_PIN);
  189. //
  190. // Return status to caller
  191. //
  192. MMRRETURN( mmr );
  193. }
  194. /****************************************************************************
  195. * @doc INTERNAL
  196. *
  197. * @api DWORD | wdmaudGetNumDevs | This function returns the number of (kernel)
  198. *
  199. * @parm UINT | DeviceType | The Device type
  200. *
  201. * @parm LPCWSTR | DeviceInterface | Pointer to a buffer containing the
  202. * device interface name of the SysAudio device for which we should
  203. * obtain the count of device of the type DeviceType
  204. *
  205. * @rdesc The number of devices.
  206. ***************************************************************************/
  207. DWORD FAR wdmaudGetNumDevs
  208. (
  209. UINT DeviceType,
  210. LPCWSTR DeviceInterface
  211. )
  212. {
  213. LPDEVICEINFO DeviceInfo;
  214. DWORD NumDevs;
  215. MMRESULT mmr;
  216. DPFASSERT(DeviceType == WaveOutDevice ||
  217. DeviceType == WaveInDevice ||
  218. DeviceType == MidiOutDevice ||
  219. DeviceType == MidiInDevice ||
  220. DeviceType == MixerDevice ||
  221. DeviceType == AuxDevice);
  222. DeviceInfo = GlobalAllocDeviceInfo(DeviceInterface);
  223. if (NULL == DeviceInfo)
  224. {
  225. MMRRETURN( MMSYSERR_NOMEM );
  226. }
  227. //
  228. // Call wdmaud.sys to get the number of devices for each
  229. // type of function.
  230. //
  231. DeviceInfo->DeviceType = DeviceType;
  232. //
  233. // Make sure that we don't take the critical section
  234. // in wdmaudIoControl (NT only)
  235. //
  236. DeviceInfo->OpenDone = 0;
  237. mmr = wdmaudIoControl(DeviceInfo,
  238. 0L,
  239. NULL,
  240. IOCTL_WDMAUD_GET_NUM_DEVS);
  241. #ifdef DEBUG
  242. if( mmr != MMSYSERR_NOERROR)
  243. DPF(DL_WARNING|FA_DEVICEIO, (szReturningErrorStr,mmr,MsgToAscii(mmr)) );
  244. #endif
  245. NumDevs = DeviceInfo->DeviceNumber;
  246. GlobalFreeDeviceInfo( DeviceInfo );
  247. //
  248. // DeviceNumber is overloaded so we don't have to map
  249. // an address into kernel mode
  250. //
  251. return MAKELONG(NumDevs, mmr);
  252. }
  253. /****************************************************************************
  254. * @doc INTERNAL
  255. *
  256. * @api DWORD | wdmaudDrvExit | This function indicates DevNode removal
  257. *
  258. * @parm UINT | DeviceType | The Device type
  259. *
  260. * @parm LPCWSTR | DeviceInterface | Pointer to a buffer containing the
  261. * device interface name of the SysAudio device that we are adding
  262. * or removing
  263. *
  264. * @rdesc The number of devices.
  265. ***************************************************************************/
  266. DWORD FAR wdmaudAddRemoveDevNode
  267. (
  268. UINT DeviceType,
  269. LPCWSTR DeviceInterface,
  270. BOOL fAdd
  271. )
  272. {
  273. LPDEVICEINFO DeviceInfo;
  274. MMRESULT mmr;
  275. DPFASSERT(DeviceType == WaveOutDevice ||
  276. DeviceType == WaveInDevice ||
  277. DeviceType == MidiOutDevice ||
  278. DeviceType == MidiInDevice ||
  279. DeviceType == MixerDevice ||
  280. DeviceType == AuxDevice);
  281. DeviceInfo = GlobalAllocDeviceInfo(DeviceInterface);
  282. if (NULL == DeviceInfo)
  283. {
  284. MMRRETURN( MMSYSERR_NOMEM );
  285. }
  286. //
  287. // Call wdmaud.sys to get the number of devices for each
  288. // type of function.
  289. //
  290. DeviceInfo->DeviceType = DeviceType;
  291. mmr = wdmaudIoControl(DeviceInfo,
  292. 0L,
  293. NULL,
  294. fAdd ?
  295. IOCTL_WDMAUD_ADD_DEVNODE :
  296. IOCTL_WDMAUD_REMOVE_DEVNODE);
  297. GlobalFreeDeviceInfo( DeviceInfo );
  298. MMRRETURN( mmr );
  299. }
  300. /****************************************************************************
  301. * @doc INTERNAL
  302. *
  303. * @api DWORD | wdmaudSetPreferredDevice | sets the preferred evice
  304. *
  305. * @parm UINT | DeviceType | The Device type
  306. *
  307. * @parm LPCWSTR | DeviceInterface | Pointer to a buffer containing the
  308. * device interface name of the SysAudio device that we are adding
  309. * or removing
  310. *
  311. * @rdesc The number of devices.
  312. ***************************************************************************/
  313. DWORD FAR wdmaudSetPreferredDevice
  314. (
  315. UINT DeviceType,
  316. UINT DeviceNumber,
  317. DWORD_PTR dwParam1,
  318. DWORD_PTR dwParam2
  319. )
  320. {
  321. LPDEVICEINFO DeviceInfo;
  322. MMRESULT mmr;
  323. DPFASSERT(DeviceType == WaveOutDevice ||
  324. DeviceType == WaveInDevice ||
  325. DeviceType == MidiOutDevice ||
  326. DeviceType == MidiInDevice ||
  327. DeviceType == MixerDevice ||
  328. DeviceType == AuxDevice);
  329. DeviceInfo = GlobalAllocDeviceInfo((LPCWSTR)dwParam2);
  330. if (NULL == DeviceInfo)
  331. {
  332. MMRRETURN( MMSYSERR_NOMEM );
  333. }
  334. DeviceInfo->DeviceType = DeviceType;
  335. DeviceInfo->DeviceNumber = DeviceNumber;
  336. DeviceInfo->dwFlags = (DWORD) dwParam1;
  337. mmr = wdmaudIoControl(DeviceInfo,
  338. 0L,
  339. NULL,
  340. IOCTL_WDMAUD_SET_PREFERRED_DEVICE);
  341. GlobalFreeDeviceInfo( DeviceInfo );
  342. MMRRETURN( mmr );
  343. }
  344. /****************************************************************************
  345. * @doc INTERNAL
  346. *
  347. * @api MMRESULT | wdmaudSetDeviceState |
  348. *
  349. * @parm DWORD | DeviceType | The Device type
  350. *
  351. * @parm ULONG | State | The state to set the device to
  352. *
  353. * @rdesc MMSYS.. return code
  354. ***************************************************************************/
  355. MMRESULT wdmaudSetDeviceState
  356. (
  357. LPDEVICEINFO DeviceInfo,
  358. ULONG State
  359. )
  360. {
  361. MMRESULT mmr;
  362. ULONG BufferCount;
  363. if( ( (mmr=IsValidDeviceInfo(DeviceInfo)) != MMSYSERR_NOERROR ) ||
  364. ( (mmr=IsValidDeviceState(DeviceInfo->DeviceState,FALSE)) != MMSYSERR_NOERROR ) )
  365. {
  366. MMRRETURN( mmr );
  367. }
  368. if (IOCTL_WDMAUD_WAVE_OUT_PLAY == State ||
  369. IOCTL_WDMAUD_WAVE_IN_RECORD == State ||
  370. IOCTL_WDMAUD_MIDI_IN_RECORD == State )
  371. {
  372. //
  373. // We need to create a thread here on NT because we need
  374. // to get notified when our IO requests complete. This
  375. // requires another thread of execution to be able to
  376. // process the completed IO.
  377. //
  378. mmr = wdmaudCreateCompletionThread ( DeviceInfo );
  379. if (MMSYSERR_NOERROR != mmr)
  380. {
  381. MMRRETURN( mmr );
  382. }
  383. DeviceInfo->DeviceState->fRunning = TRUE;
  384. IsValidDeviceState(DeviceInfo->DeviceState,TRUE);
  385. }
  386. if (IOCTL_WDMAUD_MIDI_IN_RESET == State ||
  387. IOCTL_WDMAUD_MIDI_IN_STOP == State)
  388. {
  389. CRITENTER;
  390. if (DeviceInfo->DeviceState->fRunning)
  391. {
  392. DeviceInfo->DeviceState->fRunning = FALSE;
  393. CRITLEAVE;
  394. }
  395. else
  396. {
  397. CRITLEAVE;
  398. if (IOCTL_WDMAUD_MIDI_IN_RESET == State)
  399. {
  400. return( wdmaudFreeMidiQ( DeviceInfo ) );
  401. }
  402. else
  403. {
  404. MMRRETURN( MMSYSERR_NOERROR );
  405. }
  406. }
  407. }
  408. //
  409. // Call the device to set the state. Note that some calls will wait in
  410. // kernel mode for events to complete. Thus, this thread may be pre-empted
  411. // and the waveThread or midThread routines will completely finish and unload
  412. // by the time we come back. Thus, the calls to wdmaudDestroyCompletionThread
  413. // will be no-ops.
  414. //
  415. DPF(DL_TRACE|FA_SYNC,("Setting state=%08X",State) );
  416. mmr = wdmaudIoControl(DeviceInfo,
  417. 0,
  418. NULL,
  419. State);
  420. DPF(DL_TRACE|FA_SYNC,("Done Setting state mmr=%08X",mmr) );
  421. if (MMSYSERR_NOERROR == mmr)
  422. {
  423. if ((IOCTL_WDMAUD_WAVE_OUT_PAUSE == State) ||
  424. (IOCTL_WDMAUD_WAVE_IN_STOP == State) ||
  425. (IOCTL_WDMAUD_WAVE_IN_RESET == State) )
  426. {
  427. DeviceInfo->DeviceState->fPaused = TRUE;
  428. }
  429. if ((IOCTL_WDMAUD_WAVE_OUT_PLAY == State) ||
  430. (IOCTL_WDMAUD_WAVE_OUT_RESET == State) ||
  431. (IOCTL_WDMAUD_WAVE_IN_RECORD == State) )
  432. {
  433. DeviceInfo->DeviceState->fPaused = FALSE;
  434. }
  435. }
  436. else
  437. {
  438. DPF(DL_WARNING|FA_ALL,("Error Setting State: mmr = %d", mmr ) );
  439. }
  440. if (IOCTL_WDMAUD_WAVE_OUT_RESET == State ||
  441. IOCTL_WDMAUD_WAVE_IN_RESET == State)
  442. {
  443. DeviceInfo->DeviceState->fRunning = FALSE;
  444. //
  445. // Wait for all of the pending IO to come back from the
  446. // reset operation.
  447. //
  448. mmr = wdmaudDestroyCompletionThread ( DeviceInfo );
  449. }
  450. if (IOCTL_WDMAUD_MIDI_IN_RESET == State)
  451. {
  452. mmr = wdmaudDestroyCompletionThread ( DeviceInfo );
  453. if (MMSYSERR_NOERROR == mmr)
  454. {
  455. mmr = wdmaudFreeMidiQ( DeviceInfo );
  456. for (BufferCount = 0; BufferCount < STREAM_BUFFERS; BufferCount++)
  457. {
  458. wdmaudGetMidiData( DeviceInfo, NULL );
  459. }
  460. }
  461. }
  462. else if (IOCTL_WDMAUD_MIDI_IN_STOP == State)
  463. {
  464. mmr = wdmaudDestroyCompletionThread ( DeviceInfo );
  465. if (MMSYSERR_NOERROR == mmr)
  466. {
  467. for (BufferCount = 0; BufferCount < STREAM_BUFFERS; BufferCount++)
  468. {
  469. wdmaudGetMidiData( DeviceInfo, NULL );
  470. }
  471. }
  472. }
  473. MMRRETURN( mmr );
  474. }
  475. /****************************************************************************
  476. * @doc INTERNAL
  477. *
  478. * @api MMRESULT | wdmaudGetPos |
  479. *
  480. * @parm DWORD | DeviceInfo | The Device instance structure
  481. *
  482. * @parm ULONG | | The state to set the device to
  483. *
  484. * @parm ULONG | State | The state to set the device to
  485. *
  486. * @rdesc MMSYS.. return code
  487. ***************************************************************************/
  488. MMRESULT wdmaudGetPos
  489. (
  490. LPDEVICEINFO pClient,
  491. LPMMTIME lpmmt,
  492. DWORD dwSize,
  493. UINT DeviceType
  494. )
  495. {
  496. DWORD dwPos;
  497. MMRESULT mmr;
  498. LPDEVICEINFO DeviceInfo;
  499. if (dwSize < sizeof(MMTIME))
  500. MMRRETURN( MMSYSERR_ERROR );
  501. DeviceInfo = GlobalAllocDeviceInfo(pClient->wstrDeviceInterface);
  502. if (NULL == DeviceInfo)
  503. {
  504. MMRRETURN( MMSYSERR_NOMEM );
  505. }
  506. //
  507. // Call wdmaud.sys to get the number of devices for each
  508. // type of function.
  509. //
  510. DeviceInfo->DeviceType = pClient->DeviceType;
  511. DeviceInfo->DeviceNumber = pClient->DeviceNumber;
  512. DeviceInfo->DeviceHandle = pClient->DeviceHandle;
  513. DeviceInfo->OpenDone = 0;
  514. //
  515. // Get the current position from the driver
  516. //
  517. mmr = wdmaudIoControl(DeviceInfo,
  518. sizeof(DWORD),
  519. (LPBYTE)&dwPos,
  520. DeviceType == WaveOutDevice ?
  521. IOCTL_WDMAUD_WAVE_OUT_GET_POS :
  522. IOCTL_WDMAUD_WAVE_IN_GET_POS);
  523. if (mmr == MMSYSERR_NOERROR)
  524. {
  525. //
  526. // dwPos is in bytes
  527. //
  528. if (lpmmt->wType == TIME_BYTES)
  529. {
  530. lpmmt->u.cb = dwPos;
  531. }
  532. else
  533. {
  534. lpmmt->wType = TIME_SAMPLES;
  535. if (pClient->DeviceState->cSampleBits != 0)
  536. {
  537. lpmmt->u.sample = (dwPos * 8) / pClient->DeviceState->cSampleBits;
  538. }
  539. else
  540. {
  541. mmr = MMSYSERR_ERROR;
  542. }
  543. }
  544. }
  545. GlobalFreeDeviceInfo( DeviceInfo );
  546. MMRRETURN( mmr );
  547. }