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.

448 lines
12 KiB

  1. /****************************************************************************
  2. *
  3. * drvutil.c
  4. *
  5. * Multimedia kernel driver support component (mmdrv)
  6. *
  7. * Copyright (c) 1992-1995 Microsoft Corporation
  8. *
  9. * Support functions common to multiple multi-media drivers :
  10. *
  11. * -- Open a device
  12. * -- Translate a kernel IO return code to a multi-media return code
  13. * -- Count the number of devices of a given type
  14. * -- Set and Get data synchronously to a kernel device
  15. *
  16. * History
  17. * 01-Feb-1992 - Robin Speed (RobinSp) wrote it
  18. * 04-Feb-1992 - Reviewed by SteveDav
  19. *
  20. ***************************************************************************/
  21. #include "mmdrv.h"
  22. #include <ntddwave.h>
  23. #include <ntddmidi.h>
  24. #include <ntddaux.h>
  25. /****************************************************************************
  26. * @doc INTERNAL
  27. *
  28. * @api MMRESULT | sndOpenDev | Open the kernel driver device corresponding
  29. * to a logical wave device id
  30. *
  31. * @parm UINT | DeviceType | The type of device
  32. *
  33. * @parm DWORD | dwId | The device id
  34. *
  35. * @parm PHANDLE | phDev | Where to return the kernel device handle
  36. *
  37. * @parm ACCESS_MASK | Access | The desired access
  38. *
  39. * @comm For our sound devices the only relevant access are read and
  40. * read/write. Device should ALWAYS allow opens for read unless some
  41. * resource or access-rights restriction occurs.
  42. ***************************************************************************/
  43. MMRESULT sndOpenDev(UINT DeviceType, DWORD dwId,
  44. PHANDLE phDev, DWORD Access)
  45. {
  46. WCHAR cDev[SOUND_MAX_DEVICE_NAME];
  47. WinAssert(DeviceType == WaveOutDevice ||
  48. DeviceType == WaveInDevice ||
  49. DeviceType == MidiOutDevice ||
  50. DeviceType == MidiInDevice ||
  51. DeviceType == AuxDevice);
  52. *phDev = INVALID_HANDLE_VALUE; // Always initialise the return value
  53. //
  54. // Check it's not out of range
  55. //
  56. if (dwId > SOUND_MAX_DEVICES) {
  57. return MMSYSERR_BADDEVICEID;
  58. }
  59. //
  60. // Create the device name and open it - remove '\Device'
  61. //
  62. wsprintf(cDev, L"\\\\.%ls%d",
  63. (DeviceType == WaveOutDevice ? DD_WAVE_OUT_DEVICE_NAME_U :
  64. DeviceType == WaveInDevice ? DD_WAVE_IN_DEVICE_NAME_U :
  65. DeviceType == MidiOutDevice ? DD_MIDI_OUT_DEVICE_NAME_U :
  66. DeviceType == MidiInDevice ? DD_MIDI_IN_DEVICE_NAME_U :
  67. DD_AUX_DEVICE_NAME_U) +
  68. strlen("\\Device"),
  69. dwId);
  70. *phDev = CreateFile(cDev,
  71. Access,
  72. FILE_SHARE_WRITE,
  73. NULL,
  74. OPEN_EXISTING,
  75. Access != GENERIC_READ ? FILE_FLAG_OVERLAPPED : 0,
  76. NULL);
  77. //
  78. // Check up on the driver for refusing to open
  79. // multiply for read
  80. //
  81. WinAssert(!(GetLastError() == ERROR_ACCESS_DENIED &&
  82. Access == GENERIC_READ));
  83. //
  84. // Return status to caller
  85. //
  86. return *phDev != INVALID_HANDLE_VALUE ? MMSYSERR_NOERROR : sndTranslateStatus();
  87. }
  88. /****************************************************************************
  89. * @doc INTERNAL
  90. *
  91. * @api void | sndTranslateStatus | This function translates an NT status
  92. * code into a multi-media error code as far as possible.
  93. *
  94. * @parm NTSTATUS | Status | The NT base operating system return status.
  95. *
  96. *
  97. * @rdesc The multi-media error code.
  98. ***************************************************************************/
  99. DWORD sndTranslateStatus(void)
  100. {
  101. #if DBG
  102. UINT n;
  103. switch (n=GetLastError()) {
  104. #else
  105. switch (GetLastError()) {
  106. #endif
  107. case NO_ERROR:
  108. case ERROR_IO_PENDING:
  109. return MMSYSERR_NOERROR;
  110. case ERROR_BUSY:
  111. return MMSYSERR_ALLOCATED;
  112. case ERROR_NOT_SUPPORTED:
  113. case ERROR_INVALID_FUNCTION:
  114. return MMSYSERR_NOTSUPPORTED;
  115. case ERROR_NOT_ENOUGH_MEMORY:
  116. return MMSYSERR_NOMEM;
  117. case ERROR_ACCESS_DENIED:
  118. return MMSYSERR_BADDEVICEID;
  119. case ERROR_INSUFFICIENT_BUFFER:
  120. return MMSYSERR_INVALPARAM;
  121. default:
  122. dprintf2(("sndTranslateStatus: LastError = %d", n));
  123. return MMSYSERR_ERROR;
  124. }
  125. }
  126. /****************************************************************************
  127. * @doc INTERNAL
  128. *
  129. * @api DWORD | sndGetNumDevs | This function returns the number of (kernel)
  130. *
  131. * @parm UINT | DeviceType | The Device type
  132. *
  133. * @rdesc The number of devices.
  134. ***************************************************************************/
  135. DWORD sndGetNumDevs(UINT DeviceType)
  136. {
  137. //
  138. // Look for devices until we don't find one
  139. //
  140. int i;
  141. HANDLE h;
  142. for (i=0;
  143. sndOpenDev(DeviceType, i, &h, GENERIC_READ) == MMSYSERR_NOERROR;
  144. i++) {
  145. //
  146. // Possible future work - try calling an actual function to make sure
  147. // this worked.
  148. //
  149. CloseHandle(h);
  150. }
  151. //
  152. // We incremented i each time we found a good device
  153. //
  154. return i;
  155. }
  156. /****************************************************************************
  157. * @doc INTERNAL
  158. *
  159. * @api DWORD | sndSetData | This function sets the volume given a device id
  160. * and could be used for other soft value setting
  161. * when only read access is required to the device
  162. *
  163. * @parm UINT | DeviceType | The Device type
  164. *
  165. * @parm UINT | DeviceId | The device id
  166. *
  167. * @parm UINT | Length | Length of data to set
  168. *
  169. * @parm PBYTE | Data | Data to set
  170. *
  171. * @parm ULONG | Ioctl | The Ioctl to use
  172. *
  173. * @rdesc MM... return code.
  174. ***************************************************************************/
  175. MMRESULT sndSetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data,
  176. ULONG Ioctl)
  177. {
  178. HANDLE hDev;
  179. MMRESULT mRet;
  180. DWORD BytesReturned;
  181. //
  182. // Open the wave output device
  183. //
  184. mRet = sndOpenDev(DeviceType, DeviceId, &hDev, GENERIC_READ);
  185. if (mRet != MMSYSERR_NOERROR) {
  186. return mRet;
  187. }
  188. //
  189. // Set our data.
  190. //
  191. // Setting the overlapped parameter (last) to null means we
  192. // wait until the operation completes.
  193. //
  194. mRet = DeviceIoControl(hDev, Ioctl, Data, Length, NULL, 0,
  195. &BytesReturned, NULL) ?
  196. MMSYSERR_NOERROR : sndTranslateStatus();
  197. //
  198. // Close our file and return
  199. //
  200. CloseHandle(hDev);
  201. return mRet;
  202. }
  203. /****************************************************************************
  204. * @doc INTERNAL
  205. *
  206. * @api DWORD | sndGetData | This function gets data from a given device
  207. * specified by device id when read-only access is
  208. * sufficient
  209. *
  210. * @parm UINT | DeviceType | The Device type
  211. *
  212. * @parm UINT | DeviceId | The device id
  213. *
  214. * @parm UINT | Length | Length of data to set
  215. *
  216. * @parm PBYTE | Data | Data to set
  217. *
  218. * @parm ULONG | Ioctl | The Ioctl to use
  219. *
  220. * @rdesc MM... return code.
  221. ***************************************************************************/
  222. MMRESULT sndGetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data,
  223. ULONG Ioctl)
  224. {
  225. HANDLE hDev;
  226. MMRESULT mRet;
  227. DWORD BytesReturned;
  228. //
  229. // Open the wave output device
  230. //
  231. mRet = sndOpenDev(DeviceType, DeviceId, &hDev, GENERIC_READ);
  232. if (mRet != MMSYSERR_NOERROR) {
  233. return mRet;
  234. }
  235. //
  236. // Set our data.
  237. //
  238. // Setting the overlapped parameter (last) to null means we
  239. // wait until the operation completes.
  240. //
  241. mRet = DeviceIoControl(hDev, Ioctl, NULL, 0, (LPVOID)Data, Length,
  242. &BytesReturned, NULL) ?
  243. MMSYSERR_NOERROR : sndTranslateStatus();
  244. //
  245. // Close our file and return
  246. //
  247. CloseHandle(hDev);
  248. return mRet;
  249. }
  250. /****************************************************************************
  251. * @doc INTERNAL
  252. *
  253. * @api MMRESULT | sndGetHandleData | Get data from a device using its handle
  254. *
  255. * @parm PWAVEALLOC | pClient | Client handle.
  256. *
  257. * @parm DWORD | dwSize | Size of the data
  258. *
  259. * @parm PVOID | pData | Where to put the data.
  260. *
  261. * @parm ULONG | Function | The Ioctl to use
  262. *
  263. * @rdesc MMSYS... return value.
  264. ***************************************************************************/
  265. MMRESULT sndGetHandleData(HANDLE hDev,
  266. DWORD dwSize,
  267. PVOID pData,
  268. ULONG Ioctl,
  269. HANDLE hEvent)
  270. {
  271. OVERLAPPED Overlap;
  272. DWORD BytesReturned;
  273. WinAssert(hDev != NULL);
  274. memset(&Overlap, 0, sizeof(Overlap));
  275. Overlap.hEvent = hEvent;
  276. //
  277. // Issue the IO control. We must wait with our own event because
  278. // setting the overlapped object to null will complete if other
  279. // IOs complete.
  280. //
  281. if (!DeviceIoControl(hDev,
  282. Ioctl,
  283. NULL,
  284. 0,
  285. pData,
  286. dwSize,
  287. &BytesReturned,
  288. &Overlap)) {
  289. DWORD cbTransfer;
  290. //
  291. // Wait for completion if necessary
  292. //
  293. if (GetLastError() == ERROR_IO_PENDING) {
  294. if (!GetOverlappedResult(hDev, &Overlap, &cbTransfer,
  295. TRUE)) {
  296. return sndTranslateStatus();
  297. }
  298. } else {
  299. return sndTranslateStatus();
  300. }
  301. }
  302. //
  303. // We'd better peek aleratbly to flush out any IO
  304. // completions so that things like RESET only
  305. // return when all buffers have been completed
  306. //
  307. // This relies on the fact that SleepEx will return
  308. // WAIT_IO_COMPLETION in preference to OK
  309. //
  310. while (SetEvent(hEvent) &&
  311. WaitForSingleObjectEx(hEvent, 0, TRUE) == WAIT_IO_COMPLETION) {}
  312. return MMSYSERR_NOERROR;
  313. }
  314. /****************************************************************************
  315. * @doc INTERNAL
  316. *
  317. * @api MMRESULT | sndSetHandleData | Pass data to a device using its handle
  318. *
  319. * @parm PWAVEALLOC | pClient | Client handle.
  320. *
  321. * @parm DWORD | dwSize | Size of the data
  322. *
  323. * @parm PVOID | pData | Data to send.
  324. *
  325. * @parm ULONG | Function | The Ioctl to use
  326. *
  327. * @rdesc MMSYS... return value.
  328. ***************************************************************************/
  329. MMRESULT sndSetHandleData(HANDLE hDev,
  330. DWORD dwSize,
  331. PVOID pData,
  332. ULONG Ioctl,
  333. HANDLE hEvent)
  334. {
  335. OVERLAPPED Overlap;
  336. DWORD BytesReturned;
  337. WinAssert(hDev != NULL);
  338. memset((PVOID)&Overlap, 0, sizeof(Overlap));
  339. Overlap.hEvent = hEvent;
  340. //
  341. // Issue the IO control. We must wait with our own event because
  342. // setting the overlapped object to null will complete if other
  343. // IOs complete.
  344. //
  345. if (!DeviceIoControl(hDev,
  346. Ioctl,
  347. pData,
  348. dwSize,
  349. NULL,
  350. 0,
  351. &BytesReturned,
  352. &Overlap)) {
  353. DWORD cbTransfer;
  354. //
  355. // Wait for completion if necessary
  356. //
  357. if (GetLastError() == ERROR_IO_PENDING) {
  358. if (!GetOverlappedResult(hDev, &Overlap, &cbTransfer,
  359. TRUE)) {
  360. return sndTranslateStatus();
  361. }
  362. } else {
  363. return sndTranslateStatus();
  364. }
  365. }
  366. //
  367. // We'd better peek alertably to flush out any IO
  368. // completions so that things like RESET only
  369. // return when all buffers have been completed
  370. //
  371. // This relies on the fact that SleepEx will return
  372. // WAIT_IO_COMPLETION in preference to OK
  373. //
  374. while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {}
  375. return MMSYSERR_NOERROR;
  376. }