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.

641 lines
16 KiB

  1. /*
  2. * @Doc DMusic16
  3. *
  4. * @Module Device.c - Device management routines |
  5. *
  6. * This module manages handles and handle instances to the legacy MIDI device.
  7. *
  8. * Each open device is represented by a handle (which is
  9. * an <c OPENHANDLE> struct). This struct contains all of the information
  10. * concerning the state of the device, including a reference count of the
  11. * number of clients using the device.
  12. *
  13. * Each client use of one device is represented by a handle instance (which
  14. * is an <c OPENHANDLINSTANCE> struct). A near pointer to this struct is
  15. * the actual handle seen by the client. These handle instances are used
  16. * to hold any client-specific information, and to dereference client
  17. * handles to the proper <c OPENHANDLE> struct.
  18. *
  19. * Currently we support multiple clients on the same output device but
  20. * only one client per input device.
  21. *
  22. * @globalv NPLINKNODE | gOpenHandleInstanceList | The master list of all
  23. * open handle instances.
  24. *
  25. * @globalv NPLINKNODE | gOpenHandleList | The master list of all open
  26. * handles.
  27. *
  28. * @globalv UINT | gcOpenInputDevices | A reference count of open MIDI
  29. * in devices.
  30. *
  31. * @globalv UINT | gcOpenOutputDevices | A reference count of open MIDI
  32. * out devices.
  33. */
  34. #include <windows.h>
  35. #include <mmsystem.h>
  36. #include "dmusic16.h"
  37. #include "debug.h"
  38. NPLINKNODE gOpenHandleInstanceList;
  39. NPLINKNODE gOpenHandleList;
  40. UINT gcOpenInputDevices;
  41. UINT gcOpenOutputDevices;
  42. STATIC VOID PASCAL UpdateSegmentLocks(BOOL fIsOutput);
  43. #pragma alloc_text(INIT_TEXT, DeviceOnLoad)
  44. #pragma alloc_text(FIX_COMM_TEXT, IsValidHandle)
  45. /* @func Called at DLL LibInit
  46. *
  47. * @comm
  48. *
  49. * Initialize the handle lists to empty and clear the device reference counts.
  50. */
  51. VOID PASCAL
  52. DeviceOnLoad(VOID)
  53. {
  54. gOpenHandleInstanceList = NULL;
  55. gOpenHandleList = NULL;
  56. gcOpenInputDevices = 0;
  57. gcOpenOutputDevices = 0;
  58. }
  59. /* @func Open a device
  60. *
  61. * @comm
  62. *
  63. * This function is thunked to the 32-bit peer.
  64. *
  65. * This function allocates an <c OPENHANDLEINSTANCE> struct on behalf of the caller.
  66. * If the requested device is already open and is an output device, the device's
  67. * reference count will be incremented an no other action is taken. If the requested
  68. * device is already open and is an input device, then the open will fail.
  69. *
  70. * If a non-DirectMusic application has the requested device open, then the
  71. * open will fail regardless of device type.
  72. *
  73. * If this open is the first input or output device opened, then it will
  74. * page lock the appropriate segments containing callback code and data.
  75. *
  76. * @rdesc Returns one of the following:
  77. *
  78. * @flag MMSYSERR_NOERROR | On success
  79. * @flag MMSYSERR_NOMEM | If there was insufficient memory to allocate
  80. * the tracking structure.
  81. *
  82. * @flag MMSYSERR_BADDEVICEID | If the given device ID was out of range.
  83. * @flag MMSYSERR_ALLOCATED | The specified device is already open.
  84. *
  85. */
  86. MMRESULT WINAPI
  87. OpenLegacyDevice(
  88. UINT id, /* @parm MMSYSTEM id of device to open */
  89. BOOL fIsOutput, /* @parm TRUE if this is an output device */
  90. BOOL fShare, /* @parm TRUE if the device should be shareable */
  91. LPHANDLE ph) /* @parm Pointer where handle will be returned */
  92. /* on success. */
  93. {
  94. NPOPENHANDLEINSTANCE pohi;
  95. NPLINKNODE pLink;
  96. NPOPENHANDLE poh;
  97. MMRESULT mmr;
  98. DPF(2, "OpenLegacyDevice(%d,%s,%s)",
  99. (UINT)id,
  100. (LPSTR)(fIsOutput ? "Output" : "Input"),
  101. (LPSTR)(fShare ? "Shared" : "Exclusive"));
  102. *ph = (HANDLE)NULL;
  103. /* Sharing capture device is not allowed.
  104. */
  105. if ((!fIsOutput) && (fShare))
  106. {
  107. return MMSYSERR_ALLOCATED;
  108. }
  109. /* Make sure id is in the valid range of devices
  110. */
  111. if (fIsOutput)
  112. {
  113. if (id != MIDI_MAPPER &&
  114. id >= midiOutGetNumDevs())
  115. {
  116. return MMSYSERR_BADDEVICEID;
  117. }
  118. }
  119. else
  120. {
  121. if (id >= midiInGetNumDevs())
  122. {
  123. return MMSYSERR_BADDEVICEID;
  124. }
  125. }
  126. /* Create an open handle instance. This will be returned to
  127. * Win32 as the handle.
  128. */
  129. pohi = (NPOPENHANDLEINSTANCE)LocalAlloc(LPTR, sizeof(OPENHANDLEINSTANCE));
  130. if (NULL == pohi)
  131. {
  132. return MMSYSERR_NOMEM;
  133. }
  134. /* Search through the handles we already have open and try
  135. * to find the handle already open.
  136. */
  137. mmr = MMSYSERR_NOERROR;
  138. for (pLink = gOpenHandleList; pLink; pLink = pLink->pNext)
  139. {
  140. poh = (NPOPENHANDLE)pLink;
  141. if (poh->id != id)
  142. {
  143. continue;
  144. }
  145. if ((fIsOutput && (!(poh->wFlags & OH_F_MIDIIN))) ||
  146. ((!fIsOutput) && (poh->wFlags & OH_F_MIDIIN)))
  147. {
  148. break;
  149. }
  150. }
  151. /* If we didn't find it, try to allocate it.
  152. *
  153. */
  154. if (NULL == pLink)
  155. {
  156. poh = (NPOPENHANDLE)LocalAlloc(LPTR, sizeof(OPENHANDLE));
  157. if (NULL == poh)
  158. {
  159. LocalFree((HLOCAL)pohi);
  160. return MMSYSERR_NOMEM;
  161. }
  162. poh->uReferenceCount = 1;
  163. poh->id = id;
  164. poh->wFlags = (fIsOutput ? 0 : OH_F_MIDIIN);
  165. if (fShare)
  166. {
  167. poh->wFlags |= OH_F_SHARED;
  168. }
  169. InitializeCriticalSection(&poh->wCritSect);
  170. }
  171. else
  172. {
  173. poh = (NPOPENHANDLE)pLink;
  174. /* Validate sharing modes match.
  175. * If they want exclusive mode, fail.
  176. * If the device is already open in exclusive mode, fail.
  177. */
  178. if (!fShare)
  179. {
  180. DPF(0, "Legacy open failed: non-shared open request, port already open.");
  181. LocalFree((HLOCAL)pohi);
  182. return MIDIERR_BADOPENMODE;
  183. }
  184. if (!(poh->wFlags & OH_F_SHARED))
  185. {
  186. DPF(0, "Legacy open failed: Port already open in exclusive mode.");
  187. LocalFree((HLOCAL)pohi);
  188. return MIDIERR_BADOPENMODE;
  189. }
  190. ++poh->uReferenceCount;
  191. }
  192. pohi->pHandle = poh;
  193. pohi->fActive = FALSE;
  194. pohi->wTask = GetCurrentTask();
  195. /* We lock segments here so we minimize the impacy of activation. However,
  196. * actual device open is tied to activation.
  197. */
  198. if (fIsOutput)
  199. {
  200. ++gcOpenOutputDevices;
  201. mmr = MidiOutOnOpen(pohi);
  202. if (mmr)
  203. {
  204. --gcOpenOutputDevices;
  205. }
  206. UpdateSegmentLocks(fIsOutput);
  207. }
  208. else
  209. {
  210. ++gcOpenInputDevices;
  211. mmr = MidiInOnOpen(pohi);
  212. if (mmr)
  213. {
  214. --gcOpenInputDevices;
  215. }
  216. UpdateSegmentLocks(fIsOutput);
  217. }
  218. if (poh->uReferenceCount == 1)
  219. {
  220. ListInsert(&gOpenHandleList, &poh->link);
  221. }
  222. ListInsert(&gOpenHandleInstanceList, &pohi->link);
  223. ListInsert(&poh->pInstanceList, &pohi->linkHandleList);
  224. *ph = (HANDLE)(DWORD)(WORD)pohi;
  225. return MMSYSERR_NOERROR;
  226. }
  227. /* @func Close a legacy device
  228. *
  229. * @comm
  230. *
  231. * This function is thunked to the 32-bit peer.
  232. *
  233. * It just validates the handle and calls the internal close device API.
  234. *
  235. * @rdesc Returns one of the following:
  236. *
  237. * @flag MMSYSERR_NOERROR | On success
  238. *
  239. * @flag MMSYSERR_INVALHANDLE | If the passed handle was not recognized.
  240. *
  241. */
  242. MMRESULT WINAPI
  243. CloseLegacyDevice(
  244. HANDLE h) /* @parm The handle to close. */
  245. {
  246. NPOPENHANDLEINSTANCE pohi = (NPOPENHANDLEINSTANCE)(WORD)h;
  247. DPF(2, "CloseLegacyDevice %04X\n", h);
  248. if (!IsValidHandle(h, VA_F_EITHER, &pohi))
  249. {
  250. DPF(0, "CloseLegacyDevice: Invalid handle\n");
  251. return MMSYSERR_INVALHANDLE;
  252. }
  253. return CloseLegacyDeviceI(pohi);
  254. }
  255. /* @func Activate or deactivate a legacy device
  256. *
  257. * @comm
  258. *
  259. * This function is thunked to the 32-bit peer.
  260. *
  261. * Validate parameters and pass the call to the internal activate.
  262. *
  263. * @rdesc Returns one of the following:
  264. *
  265. * @flag MMSYSERR_NOERROR | On success
  266. * @flag MMSYSERR_INVALHANDLE | If the passed handle was not recognized.
  267. * Any other MMRESULT that a midiXxx call might return.
  268. *
  269. */
  270. MMRESULT WINAPI
  271. ActivateLegacyDevice(
  272. HANDLE h,
  273. BOOL fActivate)
  274. {
  275. NPOPENHANDLEINSTANCE pohi;
  276. if (!IsValidHandle(h, VA_F_EITHER, &pohi))
  277. {
  278. DPF(0, "Activate: Invalid handle\n");
  279. return MMSYSERR_INVALHANDLE;
  280. }
  281. return ActivateLegacyDeviceI(pohi, fActivate);
  282. }
  283. /* @func Close a legacy device (internal)
  284. *
  285. * @comm
  286. *
  287. * This function deallocates the referenced <c OPENHANDLEINSTANCE> struct.
  288. * If it is the last reference to the device, then the device will be closed
  289. * as well.
  290. *
  291. * If this is the last input or output device being closed, then the
  292. * appropriate segments containing callback code and data will be
  293. * unlocked.
  294. *
  295. * @rdesc Returns one of the following:
  296. *
  297. * @flag MMSYSERR_NOERROR | On success
  298. *
  299. */
  300. MMRESULT PASCAL
  301. CloseLegacyDeviceI(
  302. NPOPENHANDLEINSTANCE pohi)
  303. {
  304. NPOPENHANDLE poh;
  305. /* Deactivate this device. This might result in the device being closed.
  306. */
  307. ActivateLegacyDeviceI(pohi, FALSE);
  308. poh = pohi->pHandle;
  309. ListRemove(&gOpenHandleInstanceList, &pohi->link);
  310. ListRemove(&poh->pInstanceList, &pohi->linkHandleList);
  311. --poh->uReferenceCount;
  312. if (poh->wFlags & OH_F_MIDIIN)
  313. {
  314. --gcOpenInputDevices;
  315. MidiInOnClose(pohi);
  316. UpdateSegmentLocks(FALSE /*fIsOutput*/);
  317. }
  318. else
  319. {
  320. --gcOpenOutputDevices;
  321. MidiOutOnClose(pohi);
  322. UpdateSegmentLocks(TRUE /*fIsOutput*/);
  323. }
  324. if (0 == poh->uReferenceCount)
  325. {
  326. ListRemove(&gOpenHandleList, &poh->link);
  327. LocalFree((HLOCAL)poh);
  328. }
  329. LocalFree((HLOCAL)pohi);
  330. return MMSYSERR_NOERROR;
  331. }
  332. /* @func Activate or deactivate a legacy device (internal)
  333. *
  334. * @comm
  335. *
  336. * This function is thunked to the 32-bit peer.
  337. *
  338. * Handle open and close of the device on first activate and last deactivate.
  339. *
  340. * @rdesc Returns one of the following:
  341. *
  342. * @flag MMSYSERR_NOERROR | On success
  343. * @flag MMSYSERR_INVALHANDLE | If the passed handle was not recognized.
  344. * Any other MMRESULT that a midiXxx call might return.
  345. *
  346. */
  347. MMRESULT PASCAL
  348. ActivateLegacyDeviceI(
  349. NPOPENHANDLEINSTANCE pohi,
  350. BOOL fActivate)
  351. {
  352. NPOPENHANDLE poh;
  353. MMRESULT mmr;
  354. poh = pohi->pHandle;
  355. if (fActivate)
  356. {
  357. if (pohi->fActive)
  358. {
  359. DPF(0, "Activate: Activating already active handle %04X", pohi);
  360. return MMSYSERR_NOERROR;
  361. }
  362. poh->uActiveCount++;
  363. if (poh->wFlags & OH_F_MIDIIN)
  364. {
  365. mmr = MidiInOnActivate(pohi);
  366. }
  367. else
  368. {
  369. mmr = MidiOutOnActivate(pohi);
  370. }
  371. if (mmr == MMSYSERR_NOERROR)
  372. {
  373. pohi->fActive = TRUE;
  374. }
  375. else
  376. {
  377. --poh->uActiveCount;
  378. }
  379. }
  380. else
  381. {
  382. if (!pohi->fActive)
  383. {
  384. DPF(0, "Activate: Deactivating already inactive handle %04X", pohi);
  385. return MMSYSERR_NOERROR;
  386. }
  387. pohi->fActive = TRUE;
  388. poh->uActiveCount--;
  389. if (poh->wFlags & OH_F_MIDIIN)
  390. {
  391. mmr = MidiInOnDeactivate(pohi);
  392. }
  393. else
  394. {
  395. mmr = MidiOutOnDeactivate(pohi);
  396. }
  397. if (mmr == MMSYSERR_NOERROR)
  398. {
  399. pohi->fActive = FALSE;
  400. }
  401. else
  402. {
  403. --poh->uActiveCount;
  404. }
  405. }
  406. return mmr;
  407. }
  408. /* @func Validate the given handle
  409. *
  410. * @comm
  411. *
  412. * Determine if the given handle is valid, and if so, return the open handle instance.
  413. *
  414. * The handle is merely a pointer to an <c OPENHANDLEINSTANCE> struct. This function,
  415. * in the debug build, will verify that the handle actually points to a struct allocated
  416. * by this DLL. In all builds, the handle type will be verified.
  417. *
  418. * @rdesc Returns one of the following:
  419. * @flag MMSYSERR_NOERROR | On success
  420. * @flag MMSYSERR_INVALHANDLE | If the given handle is invalid or of the wrong type.
  421. *
  422. */
  423. BOOL PASCAL
  424. IsValidHandle(
  425. HANDLE h, /* @parm The handle to verify */
  426. WORD wType, /* @parm The required type of handle. One of the following: */
  427. /* @flag VA_F_INPUT | If the handle must specify an input device */
  428. /* @flag VA_F_OUTPUT | If the handle must specify an output device */
  429. /* @flag VA_F_EITHER | If either type of handle is acceptable */
  430. NPOPENHANDLEINSTANCE FAR *lppohi) /* @parm Will contain the open handle instance on return */
  431. {
  432. #ifdef DEBUG
  433. NPLINKNODE pLink;
  434. #endif
  435. NPOPENHANDLEINSTANCE pohi = (NPOPENHANDLEINSTANCE)(WORD)h;
  436. #ifdef DEBUG
  437. /* Find the handle instance in the global list
  438. */
  439. for (pLink = gOpenHandleInstanceList; pLink; pLink = pLink->pNext)
  440. {
  441. DPF(2, "IsValidHandle: Theirs %04X mine %04X", (WORD)h, (WORD)pLink);
  442. if (pLink == (NPLINKNODE)(WORD)h)
  443. {
  444. break;
  445. }
  446. }
  447. if (NULL == pLink)
  448. {
  449. return FALSE;
  450. }
  451. #endif
  452. DPF(2, "IsValidHandle: Got handle, flags are %04X", pohi->pHandle->wFlags);
  453. *lppohi = pohi;
  454. /* Verify the handle type
  455. */
  456. if (pohi->pHandle->wFlags & OH_F_MIDIIN)
  457. {
  458. if (wType & VA_F_INPUT)
  459. {
  460. return TRUE;
  461. }
  462. }
  463. else
  464. {
  465. if (wType & VA_F_OUTPUT)
  466. {
  467. return TRUE;
  468. }
  469. }
  470. *lppohi = NULL;
  471. return FALSE;
  472. }
  473. /* @func Lock or unlock segments as need be.
  474. *
  475. * @comm
  476. *
  477. * This function calls the DLL's Lock and Unlock functions to bring the lock status
  478. * of the segments containing callback code and data into sync with the actual types
  479. * of devices currently open. This prevents having too much memory page locked when
  480. * it is not actually being used.
  481. *
  482. */
  483. STATIC VOID PASCAL
  484. UpdateSegmentLocks(
  485. BOOL fIsOutput) /* @parm TRUE if the last device opened or closed was an output device */
  486. {
  487. if (fIsOutput)
  488. {
  489. switch(gcOpenOutputDevices)
  490. {
  491. case 0:
  492. if (gcOpenInputDevices)
  493. {
  494. DPF(2, "Unlocking output");
  495. UnlockCode(LOCK_F_OUTPUT);
  496. }
  497. else
  498. {
  499. DPF(2, "Unlocking output+common");
  500. UnlockCode(LOCK_F_OUTPUT | LOCK_F_COMMON);
  501. }
  502. break;
  503. case 1:
  504. if (gcOpenInputDevices)
  505. {
  506. DPF(2, "Locking output");
  507. LockCode(LOCK_F_OUTPUT);
  508. }
  509. else
  510. {
  511. DPF(2, "Locking output+common");
  512. LockCode(LOCK_F_OUTPUT | LOCK_F_COMMON);
  513. }
  514. break;
  515. }
  516. }
  517. else
  518. {
  519. switch(gcOpenInputDevices)
  520. {
  521. case 0:
  522. if (gcOpenOutputDevices)
  523. {
  524. DPF(2, "Unlocking input");
  525. UnlockCode(LOCK_F_INPUT);
  526. }
  527. else
  528. {
  529. DPF(2, "Unlocking input+common");
  530. UnlockCode(LOCK_F_INPUT | LOCK_F_COMMON);
  531. }
  532. break;
  533. case 1:
  534. if (gcOpenOutputDevices)
  535. {
  536. DPF(2, "Locking input");
  537. LockCode(LOCK_F_INPUT);
  538. }
  539. else
  540. {
  541. DPF(2, "Locking input+common");
  542. LockCode(LOCK_F_INPUT | LOCK_F_COMMON);
  543. }
  544. break;
  545. }
  546. }
  547. }
  548. /* @func Clean up all open handles held by a given task
  549. *
  550. * @comm This function is called when a task terminates. It will clean up resources left
  551. * behind by a process which did not terminate cleanly, and therefore did not tell
  552. * this DLL to unload in its context.
  553. */
  554. VOID PASCAL
  555. CloseDevicesForTask(
  556. WORD wTask)
  557. {
  558. NPLINKNODE pLink;
  559. NPOPENHANDLEINSTANCE pohi;
  560. for (pLink = gOpenHandleInstanceList; pLink; pLink = pLink->pNext)
  561. {
  562. pohi = (NPOPENHANDLEINSTANCE)pLink;
  563. if (pohi->wTask != wTask)
  564. {
  565. continue;
  566. }
  567. DPF(0, "CloseDevicesForTask: Closing %04X", (WORD)pohi);
  568. /* NOTE: This will free pohi
  569. */
  570. CloseLegacyDeviceI(pohi);
  571. pLink = gOpenHandleInstanceList;
  572. }
  573. }