Leaked source code of windows server 2003
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.

1732 lines
51 KiB

  1. //
  2. // Copyright (c) 1999 Microsoft Corporation
  3. //
  4. // HIDCOM.EXE -- exploratory USB Phone Console Application
  5. //
  6. // audio.cpp -- audio magic
  7. //
  8. // Zoltan Szilagyi, July - August, 1999
  9. //
  10. // Prioritized to-do list:
  11. //
  12. // * Convert printfs to debug tracing, and define debug tracing to
  13. // printfs for HidCom.Exe use.
  14. //
  15. // * GetInstanceFromDeviceName should look only for audio devices.
  16. // This should somewhat reduce the 2 sec wave enumeration time.
  17. // Don't forget to remove timing debug output.
  18. //
  19. // * Consider changing FindWaveIdFromHardwareIdString and its helpers to take a
  20. // devinst only and computer the hardware ids on the fly rather than storing
  21. // them in arrays. This would slow it down but make the code simpler.
  22. //
  23. // * Small one-time memory leak: The static arrays of hardware ID
  24. // strings are leaked. That's a few KB per process, no increase over
  25. // time. If we make this a class then we'll just deallocate those
  26. // arrays in the destructor.
  27. // Also, for PNP events that cause us to recompute the mapping, we will
  28. // need to destroy the array at some point if the wave devices change.
  29. // So we need to augment the interface for this.
  30. //
  31. #include <wtypes.h>
  32. #include <stdio.h>
  33. #include "audio.h" // our own prototypes
  34. #include <cfgmgr32.h> // CM_ functions
  35. #include <setupapi.h> // SetupDi functions
  36. #include <mmsystem.h> // wave functions
  37. #include <initguid.h>
  38. #include <devguid.h> // device guids
  39. //
  40. // mmddkp.h -- private winmm header file
  41. // This is in nt\private\inc, but one must ssync and build in
  42. // nt\private\genx\windows\inc before it will show up.
  43. //
  44. #include <mmddkp.h>
  45. #include <crtdbg.h>
  46. #define ASSERT _ASSERTE
  47. #ifdef DBG
  48. #define STATIC
  49. #else
  50. #define STATIC static
  51. #endif
  52. extern "C"
  53. {
  54. #include "mylog.h"
  55. }
  56. //////////////////////////////////////////////////////////////////////////////
  57. //////////////////////////////////////////////////////////////////////////////
  58. //////////////////////////////////////////////////////////////////////////////
  59. //
  60. //
  61. // Private helper functions
  62. //
  63. //
  64. //////////////////////////////////////////////////////////////////////////////
  65. //////////////////////////////////////////////////////////////////////////////
  66. //////////////////////////////////////////////////////////////////////////////
  67. //////////////////////////////////////////////////////////////////////////////
  68. //
  69. // MapConfigRetToWin32
  70. //
  71. // This routine maps some CM error return codes to Win32 return codes, and
  72. // maps everything else to the value specied by dwDefault. This function is
  73. // adapted almost verbatim from the SetupAPI code.
  74. //
  75. // Arguments:
  76. // CmReturnCode - IN - specifies the ConfigMgr return code to be mapped
  77. // dwDefault - IN - specifies the default value to use if no explicit
  78. // mapping applies
  79. //
  80. // Return values:
  81. // Setup API (Win32) error code
  82. //
  83. STATIC DWORD
  84. MapConfigRetToWin32(
  85. IN CONFIGRET CmReturnCode,
  86. IN DWORD dwDefault
  87. )
  88. {
  89. switch(CmReturnCode) {
  90. case CR_SUCCESS :
  91. return NO_ERROR;
  92. case CR_CALL_NOT_IMPLEMENTED :
  93. return ERROR_CALL_NOT_IMPLEMENTED;
  94. case CR_OUT_OF_MEMORY :
  95. return ERROR_NOT_ENOUGH_MEMORY;
  96. case CR_INVALID_POINTER :
  97. return ERROR_INVALID_USER_BUFFER;
  98. case CR_INVALID_DEVINST :
  99. return ERROR_NO_SUCH_DEVINST;
  100. case CR_INVALID_DEVICE_ID :
  101. return ERROR_INVALID_DEVINST_NAME;
  102. case CR_ALREADY_SUCH_DEVINST :
  103. return ERROR_DEVINST_ALREADY_EXISTS;
  104. case CR_INVALID_REFERENCE_STRING :
  105. return ERROR_INVALID_REFERENCE_STRING;
  106. case CR_INVALID_MACHINENAME :
  107. return ERROR_INVALID_MACHINENAME;
  108. case CR_REMOTE_COMM_FAILURE :
  109. return ERROR_REMOTE_COMM_FAILURE;
  110. case CR_MACHINE_UNAVAILABLE :
  111. return ERROR_MACHINE_UNAVAILABLE;
  112. case CR_NO_CM_SERVICES :
  113. return ERROR_NO_CONFIGMGR_SERVICES;
  114. case CR_ACCESS_DENIED :
  115. return ERROR_ACCESS_DENIED;
  116. case CR_NOT_DISABLEABLE:
  117. return ERROR_NOT_DISABLEABLE;
  118. default :
  119. return dwDefault;
  120. }
  121. }
  122. //////////////////////////////////////////////////////////////////////////////
  123. //
  124. // MapConfigRetToHResult
  125. //
  126. // This routine maps some CM error return codes to HRESULT return codes, and
  127. // maps everything else to the HRESULT value E_FAIL.
  128. //
  129. // Arguments:
  130. // CmReturnCode - IN - specifies the ConfigMgr return code to be mapped
  131. //
  132. // Return values:
  133. // HRESULT error code
  134. //
  135. STATIC HRESULT
  136. MapConfigRetToHResult(
  137. IN CONFIGRET CmReturnCode
  138. )
  139. {
  140. DWORD dwWin32Error;
  141. HRESULT hr;
  142. //
  143. // Map configret --> win32
  144. //
  145. dwWin32Error = MapConfigRetToWin32(
  146. CmReturnCode,
  147. E_FAIL
  148. );
  149. //
  150. // Map win32 --> HRESULT
  151. // but don't try to map default E_FAIL, as it is not within the range for
  152. // a normal win32 error code.
  153. //
  154. if ( dwWin32Error == E_FAIL )
  155. {
  156. hr = E_FAIL;
  157. }
  158. else
  159. {
  160. hr = HRESULT_FROM_WIN32( dwWin32Error );
  161. }
  162. return hr;
  163. }
  164. //////////////////////////////////////////////////////////////////////////////
  165. //
  166. // CheckIfAncestor
  167. //
  168. // This function determines if one of the specified devnodes (the "proposed
  169. // ancestor") is an ancestor of the other specified devnode (the "proposed
  170. // descendant").
  171. //
  172. // The devnodes are arranged in a tree. If node A is an ancestor of node
  173. // B, it just means that node A is either equal to node B, or has a child
  174. // that is an ancestor of node B. This can also be stated in reverse --
  175. // node C is a descendant of node D if C is equal to D, or if C's parent
  176. // is a descendant of node D.
  177. //
  178. // The algorithm used here to determine ancestry is a straightforward
  179. // application of the definition, although the recursion is removed.
  180. //
  181. // Arguments:
  182. // dwDevInstProposedAncestor - IN - the proposed ancestor (see above)
  183. // dwDevInstProposedDescendant - IN - the proposed descendant (see above)
  184. // pfIsAncestor - OUT - returns bool value indicating if
  185. // the pa is an ancestor of the pd
  186. //
  187. // Return values:
  188. // S_OK - success
  189. // others - from CM_Get_Parent
  190. //
  191. STATIC HRESULT
  192. CheckIfAncestor(
  193. IN DWORD dwDevInstProposedAnscestor,
  194. IN DWORD dwDevInstProposedDescendant,
  195. OUT BOOL * pfIsAncestor
  196. )
  197. {
  198. ASSERT( ! IsBadWritePtr( pfIsAncestor, sizeof( BOOL ) ) );
  199. DWORD dwCurrNode;
  200. HRESULT hr;
  201. //
  202. // Initially, the current node is the proposed descendant.
  203. //
  204. dwCurrNode = dwDevInstProposedDescendant;
  205. while ( TRUE )
  206. {
  207. //
  208. // Check if this node is the proposed ancestor.
  209. // If so, the proposed ancestor is an ancestor of the
  210. // proposed descendant.
  211. //
  212. if ( dwCurrNode == dwDevInstProposedAnscestor )
  213. {
  214. *pfIsAncestor = TRUE;
  215. hr = S_OK;
  216. break;
  217. }
  218. //
  219. // Replace the current node with the current node's parent.
  220. //
  221. CONFIGRET cr;
  222. DWORD dwDevInstTemp;
  223. cr = CM_Get_Parent(
  224. & dwDevInstTemp, // out: parent's devinst dword
  225. dwCurrNode, // in: child's devinst dword
  226. 0 // in: flags: must be zero
  227. );
  228. if ( cr == CR_NO_SUCH_DEVNODE )
  229. {
  230. //
  231. // This means we've fallen off the top of the PNP tree -- the
  232. // proposed ancestor is not found in the proposed descendant's
  233. // parentage chain.
  234. //
  235. * pfIsAncestor = FALSE;
  236. hr = S_OK;
  237. break;
  238. }
  239. else if ( cr != CR_SUCCESS )
  240. {
  241. //
  242. // Some other error occured.
  243. //
  244. hr = MapConfigRetToHResult( cr );
  245. break;
  246. }
  247. dwCurrNode = dwDevInstTemp;
  248. }
  249. return hr;
  250. }
  251. //////////////////////////////////////////////////////////////////////////////
  252. //
  253. // FindClosestCommonAncestor
  254. //
  255. // Given a pair of devnodes identified by devinst DWORDs, this function
  256. // finds the devinst DWORD for the closest common ancestor of the two
  257. // devnodes.
  258. //
  259. // See CheckIfAncestor for a discussion of the concepts of ancestor and
  260. // descendant. Then, devnode C is a common ancestor of devnodes A and B if C
  261. // is an ancestor of A -AND- C is an ancestor of B. Any pair of devnodes has
  262. // at least one common ancestor, that being the root of the PNP tree. A pair
  263. // of devnodes may have more than one common ancestor. The set of common
  264. // ancestors of A and B has one UNIQUE member, called the closest common
  265. // ancestor, such that no other member of the set is a child of that node.
  266. //
  267. // You can compute the closest common ancestor of two nodes A and B by
  268. // constructing a chain of nodes going from the root of the tree to A through
  269. // all A's ancestors, and also doing the same for B. Comparing these chains
  270. // side by side, they must be the same in at least the first node (the root).
  271. // The closest common ancestor for A and B is the last node that is the same
  272. // for both chains.
  273. //
  274. // The algorithm used here is an alternative, relatively stateless approach
  275. // that can take more CPU time but uses less memory, doesn't involve any
  276. // allocations, and is much easier to write (the last being the overriding
  277. // consideration, as the PNP tree is in always shallow). The code simply walks
  278. // up A's chain of ancestors, checking if each node is an ancestor of B. The
  279. // first node for which this is true is the closest common ancestor of
  280. // A and B.
  281. //
  282. // Arguments:
  283. // dwDevInstOne - IN - the first node ('A' above)
  284. // dwDevInstTwo - IN - the other node ('B' above)
  285. // pdwDevInstResult - OUT - returns the closest common ancestor
  286. //
  287. // Return values:
  288. // S_OK - success
  289. // others - from CM_Get_Parent
  290. //
  291. STATIC HRESULT
  292. FindClosestCommonAncestor(
  293. IN DWORD dwDevInstOne,
  294. IN DWORD dwDevInstTwo,
  295. OUT DWORD * pdwDevInstResult
  296. )
  297. {
  298. ASSERT( ! IsBadWritePtr( pdwDevInstResult, sizeof( DWORD ) ) );
  299. HRESULT hr;
  300. BOOL fIsAncestor;
  301. DWORD dwDevInstCurr;
  302. //
  303. // For each node up the chain of #1's parents, starting from #1 itself...
  304. //
  305. dwDevInstCurr = dwDevInstOne;
  306. while ( TRUE )
  307. {
  308. //
  309. // Check if this node is also in the chain of #2's parents.
  310. //
  311. hr = CheckIfAncestor(
  312. dwDevInstCurr,
  313. dwDevInstTwo,
  314. & fIsAncestor
  315. );
  316. if ( FAILED(hr) )
  317. {
  318. return hr;
  319. }
  320. if ( fIsAncestor )
  321. {
  322. *pdwDevInstResult = dwDevInstCurr;
  323. return S_OK;
  324. }
  325. //
  326. // Get the next node in the chain of #1's parents.
  327. //
  328. CONFIGRET cr;
  329. DWORD dwDevInstTemp;
  330. cr = CM_Get_Parent(
  331. & dwDevInstTemp, // out: parent's devinst dword
  332. dwDevInstCurr, // in: child's devinst dword
  333. 0 // in: flags: must be zero
  334. );
  335. if ( cr != CR_SUCCESS )
  336. {
  337. //
  338. // dwDevInst has no parent, or some other error occured.
  339. //
  340. // This is always an error, because there must always
  341. // be a common parent somewhere up the chain -- the root of the PNP
  342. // tree!
  343. //
  344. return MapConfigRetToHResult( cr );
  345. }
  346. dwDevInstCurr = dwDevInstTemp;
  347. }
  348. }
  349. //////////////////////////////////////////////////////////////////////////////
  350. //
  351. // TrimHardwareIdString
  352. //
  353. // This function strips off extraneous parts of the hardware ID string as
  354. // it is expected to appear for USB devices. The remaining parts of the string
  355. // are those that identify the vendor, product, and product revision, which
  356. // are together used to match devices as belonging to the same composite or
  357. // compound device.
  358. //
  359. // (Actually, for devices A and B, it's not just A and B that are compared,
  360. // it's A and the closest common parent of A and B. This ensures that the case
  361. // of multiple identical phones in the same system is handled correctly. This
  362. // logic of course lives outside of this funtion, though.)
  363. //
  364. // As an example:
  365. // "hid\Vid_04a6&Pid_00b9&Rev_0010&Mi_04&Col01"
  366. // becomes "Vid_04a6&Pid_00b9&Rev_0010"
  367. //
  368. // Note that this function will routinely be applied to strings for non-USB
  369. // devices that will not be in the same format; that's ok, since those strings
  370. // will never match USB-generated strings, be they trimmed or not.
  371. //
  372. // Also, note that the hardware ID string as read from the registry actually
  373. // consists of multiple concatenated null-terminated strings, all terminated
  374. // by two consecutive null characters. This function just ignores strings
  375. // beyond the first, as the first contains all the info we need.
  376. //
  377. // Arguments:
  378. // wszHardwareId - IN - the string to trim (in place)
  379. //
  380. // Return values:
  381. // TRUE - the string looked like a valid USB hardware ID
  382. // FALSE - the string did not look like a valid USB hardware ID
  383. //
  384. STATIC BOOL
  385. TrimHardwareIdString(
  386. IN WCHAR * wszHardwareId
  387. )
  388. {
  389. ASSERT( ! IsBadStringPtrW( wszHardwareId, (DWORD) -1 ) );
  390. //
  391. // "volatile" is needed, otherwise the compiler blatantly ignores the
  392. // recalculation of dwSize after the first pass.
  393. //
  394. volatile DWORD dwSize;
  395. DWORD dwCurrPos;
  396. BOOL fValid = FALSE;
  397. DWORD dwNumSeparators = 0;
  398. //
  399. // Strip off leading characters up to and including the first \ from the
  400. // front. If there is no \ in the string, it is invalid.
  401. //
  402. dwSize = lstrlenW(wszHardwareId);
  403. for ( dwCurrPos = 0; dwCurrPos < dwSize; dwCurrPos ++ )
  404. {
  405. if ( wszHardwareId[ dwCurrPos ] == L'\\' )
  406. {
  407. MoveMemory(
  408. wszHardwareId, // dest
  409. wszHardwareId + dwCurrPos + 1, // source
  410. sizeof(WCHAR) * dwSize - dwCurrPos // size, in bytes
  411. );
  412. fValid = TRUE;
  413. break;
  414. }
  415. }
  416. if ( ! fValid )
  417. {
  418. return FALSE;
  419. }
  420. //
  421. // Strip off trailing characters starting from the third &.
  422. // A string with less than two & is rejected.
  423. //
  424. // Examples:
  425. //
  426. // Vid_04a6&Pid_00b9&Rev_0010&Mi_04&Col01
  427. // becomes Vid_04a6&Pid_00b9&Rev_0010
  428. //
  429. // Vid_04a6&Pid_00b9&Rev_0010&Mi_04
  430. // becomes Vid_04a6&Pid_00b9&Rev_0010
  431. //
  432. // CSC6835_DEV
  433. // is rejected
  434. //
  435. //
  436. // Must recompute size because we changed it above.
  437. // (And note that dwSize is declared as 'volatile'.)
  438. //
  439. dwSize = lstrlenW(wszHardwareId);
  440. for ( dwCurrPos = 0; dwCurrPos < dwSize; dwCurrPos ++ )
  441. {
  442. if ( wszHardwareId[ dwCurrPos ] == L'&' )
  443. {
  444. dwNumSeparators ++;
  445. if ( dwNumSeparators == 3 )
  446. {
  447. wszHardwareId[ dwCurrPos ] = L'\0';
  448. break;
  449. }
  450. }
  451. }
  452. if ( dwNumSeparators < 2 )
  453. {
  454. return FALSE;
  455. }
  456. else
  457. {
  458. return TRUE;
  459. }
  460. }
  461. //////////////////////////////////////////////////////////////////////////////
  462. //
  463. // DevInstGetIdString
  464. //
  465. // This function retrieves an id string or string set for a particular
  466. // devinst dword. The value is obtained from the registry, but the
  467. // Configuration Manager API hides the detail of where in the registry this
  468. // info lives.
  469. //
  470. // Arguments:
  471. // dwDevInst - IN - the devinst dword for which we want info
  472. // dwProperty - IN - the property to retrieve
  473. // pwszIdString - OUT - returns "new"ed Unicode string or string set.
  474. //
  475. // Return values:
  476. // S_OK - success
  477. // E_OUTOFMEMORY - out of memory during string allocation
  478. // E_UNEXPECTED - data type of returned ID is not string or mutli-string
  479. // others - from CM_Get_DevNode_Registry_PropertyW
  480. //
  481. STATIC HRESULT
  482. DevInstGetIdString(
  483. IN DWORD dwDevInst,
  484. IN DWORD dwProperty,
  485. OUT WCHAR ** pwszIdString
  486. )
  487. {
  488. const DWORD INITIAL_STRING_SIZE = 100;
  489. CONFIGRET cr;
  490. DWORD dwBufferSize = INITIAL_STRING_SIZE;
  491. DWORD dwDataType = 0;
  492. ASSERT( ! IsBadWritePtr( pwszIdString, sizeof( WCHAR * ) ) );
  493. do
  494. {
  495. //
  496. // Allocate a buffer to store the returned string.
  497. //
  498. *pwszIdString = new WCHAR[ dwBufferSize + 1 ];
  499. if ( *pwszIdString == NULL )
  500. {
  501. return E_OUTOFMEMORY;
  502. }
  503. //
  504. // Try to get the string in the registry; we may not have enough
  505. // buffer space.
  506. //
  507. cr = CM_Get_DevNode_Registry_PropertyW(
  508. dwDevInst, // IN DEVINST dnDevInst,
  509. dwProperty, // IN ULONG ulProperty,
  510. & dwDataType, // OUT PULONG pulRegDataType, OPT
  511. (void *) *pwszIdString, // OUT PVOID Buffer, OPT
  512. & dwBufferSize, // IN OUT PULONG pulLength,
  513. 0 // IN ULONG ulFlags -- must be zero
  514. );
  515. if ( cr == CR_SUCCESS )
  516. {
  517. if ( ( dwDataType != REG_MULTI_SZ ) && ( dwDataType != REG_SZ ) )
  518. {
  519. //
  520. // Value available, but it is not a string ot multi-string. Ouch!
  521. //
  522. delete ( *pwszIdString );
  523. return E_UNEXPECTED;
  524. }
  525. else
  526. {
  527. return S_OK;
  528. }
  529. }
  530. else if ( cr != CR_BUFFER_SMALL )
  531. {
  532. //
  533. // It's supposed to fail with this error code because we didn't pass in
  534. // a buffer. Failed to get registry value type and length.
  535. //
  536. delete ( *pwszIdString );
  537. return MapConfigRetToHResult( cr );
  538. }
  539. else // cr == CR_BUFFER_SMALL
  540. {
  541. delete ( *pwszIdString );
  542. //
  543. // the call filled in dwBufferSize with the needed value
  544. //
  545. }
  546. }
  547. while ( TRUE );
  548. }
  549. //////////////////////////////////////////////////////////////////////////////
  550. //
  551. // HardwareIdFromDevInst
  552. //
  553. // This function retrieves a trimmed hardware ID string for a particular
  554. // devinst dword. The value is obtained from the helper function
  555. // DevInstGetIdString(), and then trimmed using TrimHardwareIdString.
  556. //
  557. // Arguments:
  558. // dwDevInst - IN - the devinst dword for which we want info
  559. // pwszHardwareId - OUT - returns "new"ed Unicode string set
  560. //
  561. // Return values:
  562. // S_OK - success
  563. // E_FAIL - not a valid string in USB format
  564. // others - from DevInstGetIdString()
  565. //
  566. STATIC HRESULT
  567. HardwareIdFromDevInst(
  568. IN DWORD dwDevInst,
  569. OUT WCHAR ** pwszHardwareId
  570. )
  571. {
  572. ASSERT( ! IsBadWritePtr(pwszHardwareId, sizeof( WCHAR * ) ) );
  573. HRESULT hr;
  574. BOOL fValid;
  575. hr = DevInstGetIdString(
  576. dwDevInst,
  577. CM_DRP_HARDWAREID,
  578. pwszHardwareId
  579. );
  580. if ( FAILED(hr) )
  581. {
  582. return hr;
  583. }
  584. // wprintf(L"*** HardwareIdFromDevInst: devinst 0x%08x, RAW hardwareID %s\n",
  585. // dwDevInst, *pwszHardwareId);
  586. fValid = TrimHardwareIdString( *pwszHardwareId );
  587. if ( ! fValid )
  588. {
  589. delete ( * pwszHardwareId );
  590. return E_FAIL;
  591. }
  592. // wprintf(L"HardwareIdFromDevInst: devinst 0x%08x, hardwareID %s\n",
  593. // dwDevInst, *pwszHardwareId);
  594. return S_OK;
  595. }
  596. //////////////////////////////////////////////////////////////////////////////
  597. //
  598. // MatchHardwareIdInArray
  599. //
  600. // This function takes the devinst and hardware ID for a HID device and
  601. // looks in an array of devinsts and hardware IDs for wave devices to find
  602. // the correct wave id to use with the HID device. The correct wave id is
  603. // the one whose hardware ID matches the HID device's hardware ID, and whose
  604. // hardware ID matches the hardware ID for the closest common ancestor of
  605. // itself and the HID device.
  606. //
  607. // Arguments:
  608. // dwHidDevInst - IN - the devinst dword for the HID device
  609. // wszHidHardwareId - IN - the trimmed hardware ID string for the
  610. // HID device
  611. // dwNumDevices - IN - the size of the arrays -- the number of
  612. // wave ids on the system
  613. // pwszHardwareIds - IN - array of trimmed hardware id strings for
  614. // the wave devices, indexed by wave ids.
  615. // Some entries may be NULL to mark them as
  616. // invalid.
  617. // pdwDevInsts - IN - array of devinsts for wave devices,
  618. // indexed by wave ids. Some entries may be
  619. // (DWORD) -1 to mark them as invalid.
  620. // pdwMatchedWaveId - OUT - the wave id that matches the hid device
  621. //
  622. // Return values:
  623. // S_OK - the devinst was matched
  624. // E_FAIL - the devinst was not matched
  625. //
  626. STATIC HRESULT
  627. MatchHardwareIdInArray(
  628. IN DWORD dwHidDevInst,
  629. IN WCHAR * wszHidHardwareId,
  630. IN DWORD dwNumDevices,
  631. IN WCHAR ** pwszHardwareIds,
  632. IN DWORD * pdwDevInsts,
  633. OUT DWORD * pdwMatchedWaveId
  634. )
  635. {
  636. ASSERT( ! IsBadStringPtrW( wszHidHardwareId, (DWORD) -1 ) );
  637. ASSERT( ! IsBadReadPtr( pwszHardwareIds,
  638. sizeof( WCHAR * ) * dwNumDevices ) );
  639. ASSERT( ! IsBadReadPtr( pdwDevInsts,
  640. sizeof( DWORD ) * dwNumDevices ) );
  641. ASSERT( ! IsBadWritePtr( pdwMatchedWaveId, sizeof(DWORD) ) );
  642. //
  643. // For each available wave id...
  644. //
  645. DWORD dwCurrWaveId;
  646. for ( dwCurrWaveId = 0; dwCurrWaveId < dwNumDevices; dwCurrWaveId++ )
  647. {
  648. //
  649. // If this particular wave device has the same stripped hardware
  650. // ID string as what we are searching for, then we have a match.
  651. // But non-USB devices have non-parsable hardware ID strings, so
  652. // they are stored in the array as NULLs.
  653. //
  654. if ( pwszHardwareIds[ dwCurrWaveId ] != NULL )
  655. {
  656. ASSERT( ! IsBadStringPtrW( pwszHardwareIds[ dwCurrWaveId ], (DWORD) -1 ) );
  657. if ( ! lstrcmpW( pwszHardwareIds[ dwCurrWaveId ], wszHidHardwareId ) )
  658. {
  659. //
  660. // We have a match, but we must still verify if we're on the same
  661. // device, not some other device that has the same hardwareID. This
  662. // is to differentiate between multiple identical phones on the same
  663. // system.
  664. //
  665. // Note: we could make the code more complex, but avoid some work in
  666. // most cases, by only doing this if there is more than one match based
  667. // on hardwareIDs alone.
  668. //
  669. DWORD dwCommonAncestor;
  670. WCHAR * wszAncestorHardwareId;
  671. HRESULT hr;
  672. hr = FindClosestCommonAncestor(
  673. dwHidDevInst,
  674. pdwDevInsts[ dwCurrWaveId ],
  675. & dwCommonAncestor
  676. );
  677. if ( SUCCEEDED(hr) )
  678. {
  679. //
  680. // Get the hardware ID for the closest common ancestor.
  681. //
  682. hr = HardwareIdFromDevInst(
  683. dwCommonAncestor,
  684. & wszAncestorHardwareId
  685. );
  686. if ( SUCCEEDED(hr) )
  687. {
  688. //
  689. // Check if they are the same. The closest common ancestor
  690. // will be some sort of hub if the audio device is from
  691. // some other identical phone other than the one whose HID
  692. // device we are looking at.
  693. //
  694. BOOL fSame;
  695. fSame = ! lstrcmpW( wszAncestorHardwareId,
  696. wszHidHardwareId );
  697. delete wszAncestorHardwareId;
  698. if ( fSame )
  699. {
  700. *pdwMatchedWaveId = dwCurrWaveId;
  701. return S_OK;
  702. }
  703. }
  704. }
  705. }
  706. }
  707. }
  708. //
  709. // No match.
  710. //
  711. return E_FAIL;
  712. }
  713. //////////////////////////////////////////////////////////////////////////////
  714. //
  715. // GetInstanceFromDeviceName
  716. //
  717. // This function retrieves a device instance identifier based on a device
  718. // name string. This works for any device.
  719. //
  720. // Arguments:
  721. // wszName - IN - the device name string
  722. // pdwInstance - OUT - returns instance identifier
  723. //
  724. // Return values:
  725. // S_OK
  726. // various win32 errors from SetupDi fucntions
  727. //
  728. STATIC HRESULT
  729. GetInstanceFromDeviceName(
  730. IN WCHAR * wszName,
  731. OUT DWORD * pdwInstance,
  732. IN HDEVINFO hDevInfo
  733. )
  734. {
  735. ASSERT( ! IsBadStringPtrW( wszName, (DWORD) -1 ) );
  736. ASSERT( ! IsBadWritePtr( pdwInstance, sizeof(DWORD) ) );
  737. //
  738. // Get the interface data for this specific device
  739. // (based on wszName).
  740. //
  741. BOOL fSuccess;
  742. DWORD dwError;
  743. SP_DEVICE_INTERFACE_DATA interfaceData;
  744. interfaceData.cbSize = sizeof( SP_DEVICE_INTERFACE_DATA ); // required
  745. fSuccess = SetupDiOpenDeviceInterfaceW(
  746. hDevInfo, // device info set handle
  747. wszName, // name of the device
  748. 0, // flags, reserved
  749. & interfaceData // OUT: interface data
  750. );
  751. if ( ! fSuccess )
  752. {
  753. LOG((PHONESP_TRACE, "GetInstanceFromDeviceName - SetupDiOpenDeviceInterfaceW failed: %08x", GetLastError()));
  754. //
  755. // Need to clean up, but save the error code first, because
  756. // the cleanup function calls SetLastError().
  757. //
  758. dwError = GetLastError();
  759. return HRESULT_FROM_WIN32( dwError );
  760. }
  761. //
  762. // Get the interface detail data from this interface data. This provides
  763. // more detailed information,including the device instance DWORD that
  764. // we seek.
  765. //
  766. SP_DEVINFO_DATA devinfoData;
  767. devinfoData.cbSize = sizeof( SP_DEVINFO_DATA ); // required
  768. fSuccess = SetupDiGetDeviceInterfaceDetail(
  769. hDevInfo, // device info set handle
  770. & interfaceData, // device interface data structure
  771. NULL, // OPT ptr to dev name struct
  772. 0, // OPT avail size of dev name st
  773. NULL, // OPT actual size of devname st
  774. & devinfoData
  775. );
  776. if ( ! fSuccess )
  777. {
  778. //
  779. // It is normal for the above function to fail with
  780. // ERROR_INSUFFICIENT_BUFFER because we passed in NULL for the
  781. // device interface detail data (device name) structure.
  782. //
  783. dwError = GetLastError();
  784. if ( dwError != ERROR_INSUFFICIENT_BUFFER )
  785. {
  786. LOG((PHONESP_TRACE, "GetInstanceFromDeviceName - SetupDiGetDeviceInterfaceDetail failed: %08x", GetLastError()));
  787. //
  788. // Can't clean this up earlier, because it does SetLastError().
  789. //
  790. return HRESULT_FROM_WIN32( dwError );
  791. }
  792. }
  793. *pdwInstance = devinfoData.DevInst;
  794. //
  795. // Can't clean this up earlier, because it does SetLastError().
  796. //
  797. return S_OK;
  798. }
  799. //////////////////////////////////////////////////////////////////////////////
  800. //
  801. //
  802. //
  803. // This function constructs an array of devinst DWORDs and an array of
  804. // hardware ID strigs, both indexed by wave id, for either wave in or wave
  805. // out devices. The devinsts are retrieved by (1) using undocumented calls
  806. // to winmm to retrieve device name strings for each wave device, and (2)
  807. // using SetupDi calls to retrieve a DevInst DWORD for each device name
  808. // string (helper function GetInstanceFromDeviceName).
  809. //
  810. // The values are saved in an array because this process takes the bulk of the
  811. // time in the HID --> audio mapping process, and therefore finding the mapping
  812. // for several HID devices can be done in not much more time than for one HID
  813. // device, just by reusing the array.
  814. //
  815. // Arguments:
  816. // fRender - IN - if TRUE, look for wave out devices
  817. // pdwNumDevices - OUT - returns number of wave devices found
  818. // ppwszHardwareIds - OUT - returns "new"ed array of trimmed hardware id
  819. // strings. The array is indexed by wave id. If
  820. // a hardware id string cannot be determined for
  821. // a particular wave id, then the string pointer
  822. // in that position is set to NULL. Each string
  823. // is "new"ed separately.
  824. // ppdwDevInsts - OUT - returns "new"ed array of devinst DWORDs. The
  825. // array is indexed by wave id. If a devinst
  826. // cannot be determined for a particular wave id,
  827. // then the DWORD in that position is set to
  828. // (DWORD) -1.
  829. //
  830. // Return values:
  831. // S_OK - success
  832. // E_OUTOFMEMORY - not enough memory to allocate a device name string or
  833. // the return array
  834. //
  835. STATIC HRESULT
  836. ConstructWaveHardwareIdCache(
  837. IN BOOL fRender,
  838. OUT DWORD * pdwNumDevices,
  839. OUT WCHAR *** ppwszHardwareIds,
  840. OUT DWORD ** ppdwDevInsts
  841. )
  842. {
  843. ASSERT( ( fRender == TRUE ) || ( fRender == FALSE ) );
  844. ASSERT( ! IsBadWritePtr( pdwNumDevices, sizeof( DWORD ) ) );
  845. ASSERT( ! IsBadWritePtr( ppwszHardwareIds, sizeof( WCHAR ** ) ) );
  846. ASSERT( ! IsBadWritePtr( ppdwDevInsts, sizeof( DWORD * ) ) );
  847. //
  848. // Get a device info list
  849. //
  850. HDEVINFO hDevInfo;
  851. /*
  852. hDevInfo = SetupDiGetClassDevs(
  853. &GUID_DEVCLASS_MEDIA, // class GUID (which device classes?)
  854. NULL, // optional enumerator to filter
  855. NULL, // HWND (we have none)
  856. ( DIGCF_PRESENT | // only devices that are present
  857. DIGCF_PROFILE ) // only devices in this hw profile
  858. );
  859. */
  860. hDevInfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_MEDIA, NULL);
  861. if ( hDevInfo == NULL )
  862. {
  863. LOG((PHONESP_TRACE, "ConstructWaveHardwareIdCache - SetupDiCreateDeviceInfoList failed: %08x", GetLastError()));
  864. return HRESULT_FROM_WIN32(GetLastError());
  865. }
  866. //
  867. // Find the number of available wave devices.
  868. //
  869. DWORD dwNumDevices;
  870. DWORD dwCurrDevice;
  871. if ( fRender )
  872. {
  873. dwNumDevices = waveOutGetNumDevs();
  874. }
  875. else
  876. {
  877. dwNumDevices = waveInGetNumDevs();
  878. }
  879. //
  880. // Allocate space for the return arrays.
  881. //
  882. *pdwNumDevices = dwNumDevices;
  883. *ppwszHardwareIds = new LPWSTR [ dwNumDevices ];
  884. if ( (*ppwszHardwareIds) == NULL )
  885. {
  886. return E_OUTOFMEMORY;
  887. }
  888. *ppdwDevInsts = new DWORD [ dwNumDevices ];
  889. if ( (*ppdwDevInsts) == NULL )
  890. {
  891. delete *ppwszHardwareIds;
  892. *ppwszHardwareIds = NULL;
  893. return E_OUTOFMEMORY;
  894. }
  895. //
  896. // Loop over the available wave devices.
  897. //
  898. for ( dwCurrDevice = 0; dwCurrDevice < dwNumDevices; dwCurrDevice++ )
  899. {
  900. //
  901. // For failure cases, we will return NULL string and -1 devinst
  902. // for that wave id. Callers should compare against the NULL, not
  903. // the -1.
  904. //
  905. (*ppwszHardwareIds) [ dwCurrDevice ] = NULL;
  906. (*ppdwDevInsts) [ dwCurrDevice ] = -1;
  907. //
  908. // Get the size of the device path string.
  909. //
  910. MMRESULT mmresult;
  911. ULONG ulSize;
  912. if ( fRender )
  913. {
  914. mmresult = waveOutMessage( (HWAVEOUT) IntToPtr(dwCurrDevice),
  915. DRV_QUERYDEVICEINTERFACESIZE,
  916. (DWORD_PTR) & ulSize,
  917. 0
  918. );
  919. }
  920. else
  921. {
  922. mmresult = waveInMessage( (HWAVEIN) IntToPtr(dwCurrDevice),
  923. DRV_QUERYDEVICEINTERFACESIZE,
  924. (DWORD_PTR) & ulSize,
  925. 0
  926. );
  927. }
  928. if ( mmresult != MMSYSERR_NOERROR )
  929. {
  930. LOG((PHONESP_TRACE, "ConstructWaveHardwareIdCache - Could not get device string size for device %d; "
  931. "error = %d", dwCurrDevice, mmresult));
  932. }
  933. else if ( ulSize == 0 )
  934. {
  935. LOG((PHONESP_TRACE, "ConstructWaveHardwareIdCache - Got zero device string size for device %d",
  936. dwCurrDevice));
  937. }
  938. else
  939. {
  940. //
  941. // Allocate space for the device path string.
  942. //
  943. WCHAR * wszDeviceName;
  944. wszDeviceName = new WCHAR[ (ulSize / 2) + 1 ];
  945. if ( wszDeviceName == NULL )
  946. {
  947. LOG((PHONESP_TRACE, "ConstructWaveHardwareIdCache - Out of memory in device string alloc for device %d;"
  948. " requested size is %d\n", dwCurrDevice, ulSize));
  949. delete *ppwszHardwareIds;
  950. *ppwszHardwareIds = NULL;
  951. delete *ppdwDevInsts;
  952. *ppdwDevInsts = NULL;
  953. return E_OUTOFMEMORY;
  954. }
  955. //
  956. // Get the device path string from winmm.
  957. //
  958. if ( fRender )
  959. {
  960. mmresult = waveOutMessage( (HWAVEOUT) IntToPtr(dwCurrDevice),
  961. DRV_QUERYDEVICEINTERFACE,
  962. (DWORD_PTR) wszDeviceName,
  963. (DWORD_PTR) ulSize
  964. );
  965. }
  966. else
  967. {
  968. mmresult = waveInMessage( (HWAVEIN) IntToPtr(dwCurrDevice),
  969. DRV_QUERYDEVICEINTERFACE,
  970. (DWORD_PTR) wszDeviceName,
  971. (DWORD_PTR) ulSize
  972. );
  973. }
  974. if ( mmresult == MMSYSERR_NOERROR )
  975. {
  976. //
  977. // Got the string. Now retrieve a devinst dword based on the
  978. // string.
  979. //
  980. // wprintf(L"\tDevice name string for device %d is:\n"
  981. // L"\t\t%ws\n",
  982. // dwCurrDevice, wszDeviceName);
  983. HRESULT hr;
  984. DWORD dwInstance;
  985. hr = GetInstanceFromDeviceName(
  986. wszDeviceName,
  987. & dwInstance,
  988. hDevInfo
  989. );
  990. delete wszDeviceName;
  991. if ( FAILED(hr) )
  992. {
  993. LOG((PHONESP_TRACE, "ConstructWaveHardwareIdCache - Can't get instance DWORD for device %d; "
  994. "error 0x%08x\n",
  995. dwCurrDevice, hr));
  996. }
  997. else
  998. {
  999. //
  1000. // Based on the devinst dword, retrieve a trimmed
  1001. // hardware id string.
  1002. //
  1003. // printf("\tInstance DWORD for device %d is "
  1004. // "0x%08x\n",
  1005. // dwCurrDevice, dwInstance);
  1006. WCHAR * wszHardwareId;
  1007. hr = HardwareIdFromDevInst(
  1008. dwInstance,
  1009. & wszHardwareId
  1010. );
  1011. if ( SUCCEEDED(hr) )
  1012. {
  1013. (*ppwszHardwareIds) [ dwCurrDevice ] = wszHardwareId;
  1014. (*ppdwDevInsts) [ dwCurrDevice ] = dwInstance;
  1015. }
  1016. }
  1017. }
  1018. }
  1019. }
  1020. SetupDiDestroyDeviceInfoList( hDevInfo );
  1021. return S_OK;
  1022. }
  1023. //////////////////////////////////////////////////////////////////////////////
  1024. //
  1025. // FindWaveIdFromHardwareIdString
  1026. //
  1027. // This function finds the wave id for a device whose devinst and hardware id
  1028. // string are known.
  1029. //
  1030. // Constructing the mapping from waveid to devinst and hardwave id string
  1031. // takes some time, so the mapping is only constructed once for each
  1032. // direction (render/capture), via the helper function
  1033. // ConstructWaveHardwareIdCache().
  1034. //
  1035. // Thereafter, the helper function MatchHardwareIdInArray() is used to run
  1036. // the matching algorithm based on the already-computed arrays. See that
  1037. // function for a description of how the matching is done.
  1038. //
  1039. // Arguments:
  1040. // dwHidDevInst - IN - the devinst dword to match to a wave id
  1041. // wszHardwareId - IN - the hardware id string for the devinst
  1042. // fRender - IN - TRUE for wave out, FALSE for wave in
  1043. // pdwMatchedWaveId - OUT - the wave id associated with the devinst
  1044. //
  1045. // Return values:
  1046. // S_OK - success
  1047. // various errors from ConstructWaveHardwareIdCache() and
  1048. // MatchHardwareIdInArray() helper functions
  1049. //
  1050. STATIC HRESULT
  1051. FindWaveIdFromHardwareIdString(
  1052. IN DWORD dwHidDevInst,
  1053. IN WCHAR * wszHardwareId,
  1054. IN BOOL fRender,
  1055. OUT DWORD * pdwMatchedWaveId
  1056. )
  1057. {
  1058. ASSERT( ! IsBadStringPtrW( wszHardwareId, (DWORD) -1 ) );
  1059. ASSERT( ( fRender == TRUE ) || ( fRender == FALSE ) );
  1060. ASSERT( ! IsBadWritePtr(pdwMatchedWaveId, sizeof(DWORD) ) );
  1061. DWORD dwNumDevices = 0;
  1062. WCHAR ** pwszHardwareIds = NULL;
  1063. DWORD * pdwDevInsts = NULL;
  1064. HRESULT hr;
  1065. //
  1066. // Need to construct cache of render device hardware IDs.
  1067. //
  1068. hr = ConstructWaveHardwareIdCache(
  1069. fRender,
  1070. & dwNumDevices,
  1071. & pwszHardwareIds,
  1072. & pdwDevInsts
  1073. );
  1074. if ( FAILED(hr) )
  1075. {
  1076. return hr;
  1077. }
  1078. //
  1079. // The cache is ready; use it to perform the rest of the matching
  1080. // algorithm.
  1081. //
  1082. hr = MatchHardwareIdInArray(
  1083. dwHidDevInst,
  1084. wszHardwareId,
  1085. dwNumDevices,
  1086. pwszHardwareIds,
  1087. pdwDevInsts,
  1088. pdwMatchedWaveId
  1089. );
  1090. //
  1091. // Free the cache
  1092. //
  1093. for ( DWORD dwCurrDevice = 0; dwCurrDevice < dwNumDevices; dwCurrDevice++ )
  1094. {
  1095. if( pwszHardwareIds[ dwCurrDevice ] )
  1096. {
  1097. delete pwszHardwareIds[ dwCurrDevice ];
  1098. }
  1099. }
  1100. delete pwszHardwareIds;
  1101. delete pdwDevInsts;
  1102. return hr;
  1103. }
  1104. //////////////////////////////////////////////////////////////////////////////
  1105. //
  1106. // OutputDeviceInfo
  1107. //
  1108. // This function is for diagnostic purposes only.
  1109. //
  1110. // Given a devinst DWORD, this function prints the DeviceDesc string as well
  1111. // as the entire (untrimmed) hardware ID string set for the device. Example:
  1112. //
  1113. //
  1114. //
  1115. // Arguments:
  1116. // dwDesiredDevInst - IN - the devinst dword for which we want info
  1117. //
  1118. // Return values:
  1119. // none
  1120. //
  1121. STATIC void
  1122. OutputDeviceInfo(
  1123. DWORD dwDesiredDevInst
  1124. )
  1125. {
  1126. //
  1127. // Get and print the device description string.
  1128. //
  1129. HRESULT hr;
  1130. WCHAR * wszDeviceDesc;
  1131. WCHAR * wszHardwareId;
  1132. hr = DevInstGetIdString(
  1133. dwDesiredDevInst,
  1134. CM_DRP_DEVICEDESC,
  1135. & wszDeviceDesc
  1136. );
  1137. if ( FAILED(hr) )
  1138. {
  1139. LOG((PHONESP_TRACE, "OutputDeviceInfo - [can't get device description string - 0x%08x]", hr));
  1140. }
  1141. else
  1142. {
  1143. LOG((PHONESP_TRACE, "OutputDeviceInfo - [DeviceDesc: %ws]", wszDeviceDesc));
  1144. delete wszDeviceDesc;
  1145. }
  1146. //
  1147. // Get and print hardware ID string set.
  1148. //
  1149. hr = DevInstGetIdString(
  1150. dwDesiredDevInst,
  1151. CM_DRP_HARDWAREID,
  1152. & wszHardwareId
  1153. );
  1154. if ( FAILED(hr) )
  1155. {
  1156. LOG((PHONESP_TRACE, "OutputDeviceInfo - [can't get hardware id - 0x%08x]", hr));
  1157. }
  1158. else
  1159. {
  1160. //
  1161. // Print out all the values in the mutli-string.
  1162. //
  1163. WCHAR * wszCurr = wszHardwareId;
  1164. while ( wszCurr[0] != L'\0' )
  1165. {
  1166. LOG((PHONESP_TRACE, "OutputDeviceInfo - [HardwareId: %ws]", wszCurr));
  1167. wszCurr += lstrlenW(wszCurr) + 1;
  1168. }
  1169. delete wszHardwareId;
  1170. }
  1171. }
  1172. //////////////////////////////////////////////////////////////////////////////
  1173. //////////////////////////////////////////////////////////////////////////////
  1174. //////////////////////////////////////////////////////////////////////////////
  1175. //
  1176. //
  1177. // Externally-callable functions
  1178. //
  1179. //
  1180. //////////////////////////////////////////////////////////////////////////////
  1181. //////////////////////////////////////////////////////////////////////////////
  1182. //////////////////////////////////////////////////////////////////////////////
  1183. //////////////////////////////////////////////////////////////////////////////
  1184. //
  1185. // ExamineWaveDevices
  1186. //
  1187. // This function is for debugging purposes only. It enumerates audio devices
  1188. // using the Wave API and prints the device path string as well as the
  1189. // device instance DWORD for each render or capture device.
  1190. //
  1191. // Arguments:
  1192. // fRender - IN - true means examine wave out devices; false = wave in
  1193. //
  1194. // Return values:
  1195. // E_OUTOFMEMORY
  1196. // S_OK
  1197. //
  1198. HRESULT
  1199. ExamineWaveDevices(
  1200. IN BOOL fRender
  1201. )
  1202. {
  1203. ASSERT( ( fRender == TRUE ) || ( fRender == FALSE ) );
  1204. DWORD dwNumDevices;
  1205. DWORD dwCurrDevice;
  1206. //
  1207. // Get a device info list
  1208. //
  1209. HDEVINFO hDevInfo;
  1210. /*
  1211. hDevInfo = SetupDiGetClassDevs(
  1212. &GUID_DEVCLASS_MEDIA, // class GUID (which device classes?)
  1213. NULL, // optional enumerator to filter
  1214. NULL, // HWND (we have none)
  1215. ( DIGCF_PRESENT | // only devices that are present
  1216. DIGCF_PROFILE ) // only devices in this hw profile
  1217. );
  1218. */
  1219. hDevInfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_MEDIA, NULL);
  1220. if ( hDevInfo == NULL )
  1221. {
  1222. LOG((PHONESP_TRACE, "ExamineWaveDevices - SetupDiCreateDeviceInfoList failed: %08x", GetLastError()));
  1223. return HRESULT_FROM_WIN32(GetLastError());
  1224. }
  1225. //
  1226. // Loop over the available wave devices.
  1227. //
  1228. if ( fRender )
  1229. {
  1230. dwNumDevices = waveOutGetNumDevs();
  1231. }
  1232. else
  1233. {
  1234. dwNumDevices = waveInGetNumDevs();
  1235. }
  1236. LOG((PHONESP_TRACE, "ExamineWaveDevices - Found %d audio %s devices.",
  1237. dwNumDevices,
  1238. fRender ? "render" : "capture"));
  1239. for ( dwCurrDevice = 0; dwCurrDevice < dwNumDevices; dwCurrDevice++ )
  1240. {
  1241. MMRESULT mmresult;
  1242. ULONG ulSize;
  1243. //
  1244. // Get the size of the device path string.
  1245. //
  1246. if ( fRender )
  1247. {
  1248. mmresult = waveOutMessage( (HWAVEOUT) IntToPtr(dwCurrDevice),
  1249. DRV_QUERYDEVICEINTERFACESIZE,
  1250. (DWORD_PTR) & ulSize,
  1251. 0
  1252. );
  1253. }
  1254. else
  1255. {
  1256. mmresult = waveInMessage( (HWAVEIN) IntToPtr(dwCurrDevice),
  1257. DRV_QUERYDEVICEINTERFACESIZE,
  1258. (DWORD_PTR) & ulSize,
  1259. 0
  1260. );
  1261. }
  1262. if ( mmresult != MMSYSERR_NOERROR )
  1263. {
  1264. LOG((PHONESP_TRACE, "ExamineWaveDevices - Could not get device string size for device %d; "
  1265. "error = %d\n", dwCurrDevice, mmresult));
  1266. }
  1267. else if ( ulSize == 0 )
  1268. {
  1269. LOG((PHONESP_TRACE, "ExamineWaveDevices - Got zero device string size for device %d\n",
  1270. dwCurrDevice));
  1271. }
  1272. else
  1273. {
  1274. //
  1275. // Allocate space for the device path string.
  1276. //
  1277. WCHAR * wszDeviceName;
  1278. wszDeviceName = new WCHAR[ (ulSize / 2) + 1 ];
  1279. if ( wszDeviceName == NULL )
  1280. {
  1281. LOG((PHONESP_TRACE, "ExamineWaveDevices - Out of memory in device string alloc for device %d;"
  1282. " requested size is %d\n", dwCurrDevice, ulSize));
  1283. return E_OUTOFMEMORY;
  1284. }
  1285. //
  1286. // Get the device path string from winmm.
  1287. //
  1288. if ( fRender )
  1289. {
  1290. mmresult = waveOutMessage( (HWAVEOUT) IntToPtr(dwCurrDevice),
  1291. DRV_QUERYDEVICEINTERFACE,
  1292. (DWORD_PTR) wszDeviceName,
  1293. ulSize
  1294. );
  1295. }
  1296. else
  1297. {
  1298. mmresult = waveInMessage( (HWAVEIN) IntToPtr(dwCurrDevice),
  1299. DRV_QUERYDEVICEINTERFACE,
  1300. (DWORD_PTR) wszDeviceName,
  1301. ulSize
  1302. );
  1303. }
  1304. if ( mmresult == MMSYSERR_NOERROR )
  1305. {
  1306. //
  1307. // Got the string; print it and convert it to a
  1308. // devinst DWORD.
  1309. //
  1310. LOG((PHONESP_TRACE, "ExamineWaveDevices - Device name string for device %d is: %ws",
  1311. dwCurrDevice, wszDeviceName));
  1312. HRESULT hr;
  1313. DWORD dwInstance;
  1314. hr = GetInstanceFromDeviceName(
  1315. wszDeviceName,
  1316. & dwInstance,
  1317. hDevInfo
  1318. );
  1319. if ( FAILED(hr) )
  1320. {
  1321. LOG((PHONESP_TRACE, "ExamineWaveDevices - Can't get instance DWORD for device %d; "
  1322. "error 0x%08x",
  1323. dwCurrDevice, hr));
  1324. }
  1325. else
  1326. {
  1327. LOG((PHONESP_TRACE, "ExamineWaveDevices - Instance DWORD for device %d is "
  1328. "0x%08x",
  1329. dwCurrDevice, dwInstance));
  1330. //
  1331. // Print various other info about this device.
  1332. //
  1333. OutputDeviceInfo( dwInstance );
  1334. WCHAR * wszHardwareId;
  1335. hr = HardwareIdFromDevInst(
  1336. dwInstance,
  1337. & wszHardwareId
  1338. );
  1339. if ( FAILED(hr) )
  1340. {
  1341. LOG((PHONESP_TRACE, "ExamineWaveDevices - Can't get hardware id string for device %d; "
  1342. "error 0x%08x",
  1343. dwCurrDevice, hr));
  1344. }
  1345. else
  1346. {
  1347. LOG((PHONESP_TRACE, "ExamineWaveDevices - Hardware ID for device %d is %ws\n",
  1348. dwCurrDevice, wszHardwareId));
  1349. delete wszHardwareId;
  1350. }
  1351. }
  1352. delete wszDeviceName;
  1353. }
  1354. }
  1355. }
  1356. SetupDiDestroyDeviceInfoList( hDevInfo );
  1357. return S_OK;
  1358. }
  1359. //////////////////////////////////////////////////////////////////////////////
  1360. //
  1361. // DiscoverAssociatedWaveId
  1362. //
  1363. // This function searches for a wave device to match the HID device in the
  1364. // PNP tree location specified in the passed in SP_DEVICE_INTERFACE_DATA
  1365. // structure, obtained from the SetupKi API. It returns the wave id for
  1366. // the matched device.
  1367. //
  1368. // It uses the helper function FindWaveIdFromHardwareIdString() to search for
  1369. // the wave device based on a devinst DWORD and a hardware ID string. First,
  1370. // it must obtain the devinst for the device; it does this by calling a SetupDi
  1371. // function and looking up the devinst in a resulting structure. The hardware
  1372. // ID string is then retrieved from the registry and trimmed, using the helper
  1373. // function HardwareIdFromDevinst().
  1374. //
  1375. // See FindWaveIdFromHardwareIdString() for further comments on the search
  1376. // algorithm.
  1377. //
  1378. // Arguments:
  1379. // dwDevInst - IN - Device Instance of the HID device
  1380. // fRender - IN - TRUE for wave out, FALSE for wave in
  1381. // pdwWaveId - OUT - the wave id associated with this HID device
  1382. //
  1383. // Return values:
  1384. // S_OK - succeeded and matched wave id
  1385. // other from helper functions FindWaveIdFromHardwareIdString() or
  1386. // or HardwareIdFromDevinst()
  1387. //
  1388. HRESULT
  1389. DiscoverAssociatedWaveId(
  1390. IN DWORD dwDevInst,
  1391. IN BOOL fRender,
  1392. OUT DWORD * pdwWaveId
  1393. )
  1394. {
  1395. ASSERT( ! IsBadWritePtr(pdwWaveId, sizeof(DWORD) ) );
  1396. ASSERT( ( fRender == TRUE ) || ( fRender == FALSE ) );
  1397. //
  1398. // We've got the device instance DWORD for the HID device.
  1399. // Use it to get the trimmed hardware ID string, which tells
  1400. // us the vendor, product, and revision numbers.
  1401. //
  1402. HRESULT hr;
  1403. WCHAR * wszHardwareId;
  1404. hr = HardwareIdFromDevInst(
  1405. dwDevInst,
  1406. & wszHardwareId
  1407. );
  1408. if ( FAILED(hr) )
  1409. {
  1410. return hr;
  1411. }
  1412. //
  1413. // Finally, use this information to choose a wave id.
  1414. //
  1415. hr = FindWaveIdFromHardwareIdString(
  1416. dwDevInst,
  1417. wszHardwareId,
  1418. fRender,
  1419. pdwWaveId
  1420. );
  1421. delete wszHardwareId;
  1422. if ( FAILED(hr) )
  1423. {
  1424. return hr;
  1425. }
  1426. return S_OK;
  1427. }
  1428. //
  1429. // eof
  1430. //