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.

2537 lines
82 KiB

  1. /** FILE: ports.c ********** Module Header ********************************
  2. *
  3. * Class installer for the Ports class.
  4. *
  5. @@BEGIN_DDKSPLIT
  6. * History:
  7. * 12:30 on Tues 23 Apr 1991 -by- Steve Cathcart [stevecat]
  8. * Took base code from Win 3.1 source
  9. * 10:30 on Tues 04 Feb 1992 -by- Steve Cathcart [stevecat]
  10. * Updated code to latest Win 3.1 sources
  11. * 16:30 on Fri 27 Mar 1992 -by- Steve Cathcart [stevecat]
  12. * Changed to allow for unlimited number of NT COM ports
  13. * 18:00 on Tue 06 Apr 1993 -by- Steve Cathcart [stevecat]
  14. * Updated to work seamlessly with NT serial driver
  15. * 19:00 on Wed 05 Jan 1994 -by- Steve Cathcart [stevecat]
  16. * Allow setting COM1 - COM4 advanced parameters
  17. @@END_DDKSPLIT
  18. *
  19. * Copyright (C) 1990-1999 Microsoft Corporation
  20. *
  21. *************************************************************************/
  22. //==========================================================================
  23. // Include files
  24. //==========================================================================
  25. // C Runtime
  26. #include <stddef.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. // Application specific
  30. #include "ports.h"
  31. #include <msports.h>
  32. // @@BEGIN_DDKSPLIT
  33. #include <initguid.h>
  34. //
  35. // Instantiate GUID_NULL.
  36. //
  37. DEFINE_GUID(GUID_NULL, 0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  38. //
  39. // Instantiate class installer GUIDs (interesting one is GUID_DEVCLASS_PORTS).
  40. //
  41. #include <devguid.h>
  42. // @@END_DDKSPLIT
  43. //==========================================================================
  44. // Globals
  45. //==========================================================================
  46. HANDLE g_hInst = NULL;
  47. TCHAR g_szClose[ 40 ]; // "Close" string
  48. TCHAR g_szErrMem[ 200 ]; // Low memory message
  49. TCHAR g_szPortsApplet[ 30 ]; // "Ports Control Panel Applet" title
  50. TCHAR g_szNull[] = TEXT(""); // Null string
  51. TCHAR m_szColon[] = TEXT( ":" );
  52. TCHAR m_szComma[] = TEXT( "," );
  53. TCHAR m_szCloseParen[] = TEXT( ")" );
  54. TCHAR m_szPorts[] = TEXT( "Ports" );
  55. TCHAR m_szCOM[] = TEXT( "COM" );
  56. TCHAR m_szSERIAL[] = TEXT( "Serial" );
  57. TCHAR m_szLPT[] = TEXT( "LPT" );
  58. //
  59. // NT Registry keys to find COM port to Serial Device mapping
  60. //
  61. TCHAR m_szRegSerialMap[] = TEXT( "Hardware\\DeviceMap\\SerialComm" );
  62. TCHAR m_szRegParallelMap[] = TEXT( "Hardware\\DeviceMap\\PARALLEL PORTS" );
  63. //
  64. // Registry Serial Port Advanced I/O settings key and valuenames
  65. //
  66. TCHAR m_szRegServices[] =
  67. TEXT( "System\\CurrentControlSet\\Services\\" );
  68. TCHAR m_szRootEnumName[] = REGSTR_KEY_ROOTENUM;
  69. TCHAR m_szAcpiEnumName[] = REGSTR_KEY_ACPIENUM;
  70. TCHAR m_szFIFO[] = TEXT( "ForceFifoEnable" );
  71. TCHAR m_szDosDev[] = TEXT( "DosDevices" );
  72. TCHAR m_szPollingPeriod[] = TEXT( "PollingPeriod" );
  73. TCHAR m_szPortName[] = REGSTR_VAL_PORTNAME;
  74. TCHAR m_szDosDeviceName[] = TEXT( "DosDeviceName" );
  75. TCHAR m_szFirmwareIdentified[] = TEXT( "FirmwareIdentified" );
  76. TCHAR m_szPortSubClass[] = REGSTR_VAL_PORTSUBCLASS;
  77. int m_nBaudRates[] = { 75, 110, 134, 150, 300, 600, 1200, 1800, 2400,
  78. 4800, 7200, 9600, 14400, 19200, 38400, 57600,
  79. 115200, 128000, 0 };
  80. TCHAR m_sz9600[] = TEXT( "9600" );
  81. TCHAR m_szDefParams[] = TEXT( "9600,n,8,1" );
  82. short m_nDataBits[] = { 4, 5, 6, 7, 8, 0};
  83. TCHAR *m_pszParitySuf[] = { TEXT( ",e" ),
  84. TEXT( ",o" ),
  85. TEXT( ",n" ),
  86. TEXT( ",m" ),
  87. TEXT( ",s" ) };
  88. TCHAR *m_pszLenSuf[] = { TEXT( ",4" ),
  89. TEXT( ",5" ),
  90. TEXT( ",6" ),
  91. TEXT( ",7" ),
  92. TEXT( ",8" ) };
  93. TCHAR *m_pszStopSuf[] = { TEXT( ",1" ),
  94. TEXT( ",1.5" ),
  95. TEXT( ",2 " ) };
  96. TCHAR *m_pszFlowSuf[] = { TEXT( ",x" ),
  97. TEXT( ",p" ),
  98. TEXT( " " ) };
  99. // @@BEGIN_DDKSPLIT
  100. //
  101. // Include the string-ified form of the Computer (i.e., HAL) class GUID here,
  102. // so we don't have to pull in OLE or RPC just to get StringFromGuid.
  103. //
  104. TCHAR m_szComputerClassGuidString[] = TEXT( "{4D36E966-E325-11CE-BFC1-08002BE10318}" );
  105. //
  106. // String to append onto install section for COM ports in order to generate
  107. // "PosDup" section.
  108. //
  109. TCHAR m_szPosDupSectionSuffix[] = (TEXT(".") INFSTR_SUBKEY_POSSIBLEDUPS);
  110. //
  111. // see GetDetectedSerialPortsList
  112. //
  113. TCHAR *m_pszSerialPnPIds[] = { TEXT( "*PNP0501" ) };
  114. #define SERIAL_PNP_IDS_COUNT (sizeof(m_pszSerialPnPIds) / sizeof(m_pszSerialPnPIds[0]))
  115. #define PARALLEL_MAX_NUMBER 3
  116. // @@END_DDKSPLIT
  117. #define IN_RANGE(value, minval, maxval) ((minval) <= (value) && (value) <= (maxval))
  118. //==========================================================================
  119. // Local Function Prototypes
  120. //==========================================================================
  121. DWORD
  122. InstallPnPSerialPort(
  123. IN HDEVINFO DeviceInfoSet,
  124. IN PSP_DEVINFO_DATA DeviceInfoData
  125. );
  126. // @@BEGIN_DDKSPLIT
  127. DWORD
  128. GetDetectedSerialPortsList(
  129. IN HDEVINFO DeviceInfoSet,
  130. IN BOOL FirstTimeSetup
  131. );
  132. DWORD
  133. RegisterDetectedSerialPort(
  134. IN HDEVINFO DeviceInfoSet,
  135. IN PSP_DEVINFO_DATA DeviceInfoData
  136. );
  137. BOOL
  138. GetPosDupList(
  139. IN HDEVINFO DeviceInfoSet,
  140. IN PSP_DEVINFO_DATA DeviceInfoData,
  141. OUT PTSTR **PosDupList,
  142. OUT INT *PosDupCount
  143. );
  144. // @@END_DDKSPLIT
  145. DWORD
  146. InstallPnPParallelPort(
  147. IN HDEVINFO DeviceInfoSet,
  148. IN PSP_DEVINFO_DATA DeviceInfoData
  149. );
  150. DWORD
  151. InstallSerialOrParallelPort(
  152. IN HDEVINFO DeviceInfoSet,
  153. IN PSP_DEVINFO_DATA DeviceInfoData
  154. );
  155. // @@BEGIN_DDKSPLIT
  156. BOOL
  157. GetSerialPortDevInstConfig(
  158. IN DEVINST DevInst,
  159. IN ULONG LogConfigType,
  160. OUT PIO_RESOURCE IoResource, OPTIONAL
  161. OUT PIRQ_RESOURCE IrqResource OPTIONAL
  162. );
  163. BOOL
  164. ChangeServiceStartType(
  165. IN PCTSTR ServiceName
  166. );
  167. // @@END_DDKSPLIT
  168. //==========================================================================
  169. // Dll Entry Point
  170. //==========================================================================
  171. BOOL APIENTRY LibMain( HANDLE hDll, DWORD dwReason, LPVOID lpReserved )
  172. {
  173. switch( dwReason )
  174. {
  175. case DLL_PROCESS_ATTACH:
  176. g_hInst = hDll;
  177. DisableThreadLibraryCalls(hDll);
  178. InitStrings();
  179. break;
  180. case DLL_PROCESS_DETACH:
  181. break;
  182. default:
  183. break;
  184. }
  185. return TRUE;
  186. }
  187. //==========================================================================
  188. // Functions
  189. //==========================================================================
  190. DWORD
  191. WINAPI
  192. PortsClassInstaller(
  193. IN DI_FUNCTION InstallFunction,
  194. IN HDEVINFO DeviceInfoSet,
  195. IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
  196. )
  197. /*++
  198. Routine Description:
  199. This routine acts as the class installer for Ports devices.
  200. Arguments:
  201. InstallFunction - Specifies the device installer function code indicating
  202. the action being performed.
  203. DeviceInfoSet - Supplies a handle to the device information set being
  204. acted upon by this install action.
  205. DeviceInfoData - Optionally, supplies the address of a device information
  206. element being acted upon by this install action.
  207. Return Value:
  208. If this function successfully completed the requested action, the return
  209. value is NO_ERROR.
  210. If the default behavior is to be performed for the requested action, the
  211. return value is ERROR_DI_DO_DEFAULT.
  212. If an error occurred while attempting to perform the requested action, a
  213. Win32 error code is returned.
  214. --*/
  215. {
  216. SP_INSTALLWIZARD_DATA iwd;
  217. HKEY hDeviceKey;
  218. HCOMDB hComDB;
  219. DWORD PortNameSize,
  220. Err,
  221. size;
  222. TCHAR PortName[20];
  223. BOOL result;
  224. switch(InstallFunction) {
  225. case DIF_INSTALLDEVICE :
  226. return InstallSerialOrParallelPort(DeviceInfoSet, DeviceInfoData);
  227. case DIF_REMOVE:
  228. if (PortTypeSerial == GetPortType(DeviceInfoSet, DeviceInfoData, FALSE)) {
  229. if (ComDBOpen(&hComDB) == ERROR_SUCCESS) {
  230. hDeviceKey = SetupDiOpenDevRegKey(DeviceInfoSet,
  231. DeviceInfoData,
  232. DICS_FLAG_GLOBAL,
  233. 0,
  234. DIREG_DEV,
  235. KEY_READ);
  236. if (hDeviceKey != INVALID_HANDLE_VALUE) {
  237. PortNameSize = sizeof(PortName);
  238. Err = RegQueryValueEx(hDeviceKey,
  239. m_szPortName,
  240. NULL,
  241. NULL,
  242. (PBYTE)PortName,
  243. &PortNameSize
  244. );
  245. RegCloseKey(hDeviceKey);
  246. if (Err == ERROR_SUCCESS) {
  247. ComDBReleasePort(hComDB,
  248. myatoi(PortName+wcslen(m_szCOM)));
  249. }
  250. }
  251. ComDBClose(hComDB);
  252. }
  253. }
  254. if (!SetupDiRemoveDevice(DeviceInfoSet, DeviceInfoData)) {
  255. return GetLastError();
  256. }
  257. return NO_ERROR;
  258. // @@BEGIN_DDKSPLIT
  259. case DIF_FIRSTTIMESETUP:
  260. //
  261. // Change Start type for serial.sys on legacy free machines.
  262. //
  263. ChangeServiceStartType(TEXT("serial"));
  264. ChangeServiceStartType(TEXT("parport"));
  265. //
  266. // FALL THROUGH...
  267. //
  268. case DIF_DETECT:
  269. return GetDetectedSerialPortsList(DeviceInfoSet,
  270. (InstallFunction == DIF_FIRSTTIMESETUP)
  271. );
  272. case DIF_REGISTERDEVICE:
  273. return RegisterDetectedSerialPort(DeviceInfoSet,
  274. DeviceInfoData
  275. );
  276. // @@END_DDKSPLIT
  277. default :
  278. //
  279. // Just do the default action.
  280. //
  281. return ERROR_DI_DO_DEFAULT;
  282. }
  283. }
  284. // @@BEGIN_DDKSPLIT
  285. BOOL
  286. ChangeServiceStartType(
  287. IN PCTSTR ServiceName
  288. )
  289. /*++
  290. Routine Description:
  291. This routine changes the start type of the passed in Service to
  292. SERVICE_DEMAND_START if the system is legacy free.
  293. Arguments:
  294. ServiceName - Service whose start type will be changed.
  295. Return Value:
  296. TRUE is the service type was changed, else FALSE.
  297. --*/
  298. {
  299. HKEY hKey;
  300. SC_HANDLE scmHandle, serviceHandle;
  301. BOOL legacyFree, serviceTypeChanged;
  302. DWORD bootArchitecture, dwSize;
  303. //
  304. // Check if this system is legacy free or not..
  305. //
  306. serviceTypeChanged = FALSE;
  307. legacyFree = FALSE;
  308. if (RegOpenKey(
  309. HKEY_LOCAL_MACHINE,
  310. TEXT("HARDWARE\\DESCRIPTION\\System"),
  311. &hKey
  312. ) == ERROR_SUCCESS) {
  313. //
  314. // According to ACPI spec, absence of bit 0 means legacy free!!!
  315. // Default to no legacy free.
  316. //
  317. bootArchitecture = 1;
  318. dwSize = sizeof(bootArchitecture);
  319. RegQueryValueEx(
  320. hKey,
  321. TEXT("BootArchitecture"),
  322. NULL,
  323. NULL,
  324. (LPBYTE)&bootArchitecture,
  325. &dwSize
  326. );
  327. if (!(bootArchitecture & 1)) {
  328. legacyFree = TRUE;
  329. }
  330. RegCloseKey(hKey);
  331. }
  332. //
  333. // For legacy free systems, change the service start-type to DemandStart (3).
  334. //
  335. if (legacyFree) {
  336. scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  337. if (scmHandle) {
  338. serviceHandle = OpenService(scmHandle, ServiceName, SERVICE_ALL_ACCESS);
  339. if (serviceHandle) {
  340. serviceTypeChanged = ChangeServiceConfig(
  341. serviceHandle,
  342. SERVICE_NO_CHANGE,
  343. SERVICE_DEMAND_START,
  344. SERVICE_NO_CHANGE,
  345. NULL,
  346. NULL,
  347. NULL,
  348. NULL,
  349. NULL,
  350. NULL,
  351. NULL
  352. );
  353. CloseServiceHandle(serviceHandle);
  354. }
  355. CloseServiceHandle(scmHandle);
  356. }
  357. }
  358. return serviceTypeChanged;
  359. }
  360. // @@END_DDKSPLIT
  361. DWORD
  362. InstallSerialOrParallelPort(
  363. IN HDEVINFO DeviceInfoSet,
  364. IN PSP_DEVINFO_DATA DeviceInfoData
  365. )
  366. /*++
  367. Routine Description:
  368. This routine installs either a serial or a parallel port.
  369. Arguments:
  370. DeviceInfoSet - Supplies a handle to the device information set containing
  371. the device being installed.
  372. DeviceInfoData - Supplies the address of the device information element
  373. being installed.
  374. Return Value:
  375. If successful, the return value is NO_ERROR, otherwise it is a Win32 error code.
  376. --*/
  377. {
  378. switch (GetPortType(DeviceInfoSet, DeviceInfoData, TRUE)) {
  379. case PortTypeParallel:
  380. return InstallPnPParallelPort(DeviceInfoSet, DeviceInfoData);
  381. case PortTypeSerial:
  382. return InstallPnPSerialPort(DeviceInfoSet, DeviceInfoData);
  383. default:
  384. return ERROR_DI_DO_DEFAULT;
  385. }
  386. }
  387. PortType
  388. GetPortType(
  389. IN HDEVINFO DeviceInfoSet,
  390. IN PSP_DEVINFO_DATA DeviceInfoData,
  391. IN BOOLEAN DoDrvKeyInstall
  392. )
  393. /*++
  394. Routine Description:
  395. This routine determines whether the driver node selected for the specified device
  396. is for a parallel (LPT or ECP) or serial (COM) port. It knows which is which by
  397. running the AddReg entries in the driver node's install section, and then looking
  398. in the devnode's driver key for a 'PortSubClass' value entry. If this value is
  399. present, and set to 0, then this is an LPT or ECP port, otherwise we treat it like
  400. a COM port. This value was relied upon in Win9x, so it is the safest way for us
  401. to make this determination.
  402. Arguments:
  403. DeviceInfoSet - Supplies a handle to the device information set containing
  404. the device being installed.
  405. DeviceInfoData - Supplies the address of the device information element
  406. being installed.
  407. Return Value:
  408. If the device is an LPT or ECP port, the return value is nonzero, otherwise it is
  409. FALSE. (If anything goes wrong, the default is to return FALSE.)
  410. --*/
  411. {
  412. SP_DRVINFO_DATA DriverInfoData;
  413. SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
  414. HINF hInf;
  415. HKEY hkDrv;
  416. TCHAR ActualInfSection[LINE_LEN];
  417. DWORD RegDataType;
  418. BYTE RegData;
  419. DWORD RegDataSize;
  420. PortType portType;
  421. ULONG err;
  422. portType = PortTypeSerial;
  423. hInf = INVALID_HANDLE_VALUE;
  424. hkDrv = 0;
  425. RegData = 0;
  426. do {
  427. //
  428. // Open up the driver key for this device so we can run our INF registry mods
  429. // against it.
  430. //
  431. hkDrv = SetupDiCreateDevRegKey(DeviceInfoSet,
  432. DeviceInfoData,
  433. DICS_FLAG_GLOBAL,
  434. 0,
  435. DIREG_DRV,
  436. NULL,
  437. NULL);
  438. if (hkDrv == 0) {
  439. break;
  440. }
  441. if (DoDrvKeyInstall) {
  442. //
  443. // Retrieve information about the driver node selected for this
  444. // device.
  445. //
  446. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  447. if (!SetupDiGetSelectedDriver(DeviceInfoSet,
  448. DeviceInfoData,
  449. &DriverInfoData)) {
  450. break;
  451. }
  452. DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
  453. if(!SetupDiGetDriverInfoDetail(DeviceInfoSet,
  454. DeviceInfoData,
  455. &DriverInfoData,
  456. &DriverInfoDetailData,
  457. sizeof(DriverInfoDetailData),
  458. NULL)
  459. && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
  460. //
  461. // For some reason we couldn't get detail data--this should
  462. // never happen.
  463. //
  464. break;
  465. }
  466. //
  467. // Open the INF that installs this driver node, so we can 'pre-run'
  468. // the AddReg entries in its install section.
  469. //
  470. hInf = SetupOpenInfFile(DriverInfoDetailData.InfFileName,
  471. NULL,
  472. INF_STYLE_WIN4,
  473. NULL);
  474. if (hInf == INVALID_HANDLE_VALUE) {
  475. //
  476. // For some reason we couldn't open the INF--this should never
  477. // happen.
  478. //
  479. break;
  480. }
  481. //
  482. // Now find the actual (potentially OS/platform-specific) install
  483. // section name.
  484. //
  485. SetupDiGetActualSectionToInstall(
  486. hInf,
  487. DriverInfoDetailData.SectionName,
  488. ActualInfSection,
  489. sizeof(ActualInfSection) / sizeof(TCHAR),
  490. NULL,
  491. NULL);
  492. //
  493. // Now run the registry modification (AddReg/DelReg) entries in
  494. // this section...
  495. //
  496. SetupInstallFromInfSection(
  497. NULL, // no UI, so don't need to specify window handle
  498. hInf,
  499. ActualInfSection,
  500. SPINST_REGISTRY,
  501. hkDrv,
  502. NULL,
  503. 0,
  504. NULL,
  505. NULL,
  506. NULL,
  507. NULL);
  508. }
  509. //
  510. // Check for a REG_BINARY (1 byte) 'PortSubClassOther' value entry first
  511. //
  512. RegDataSize = sizeof(RegData);
  513. err = RegQueryValueEx(hkDrv,
  514. TEXT("PortSubClassOther"),
  515. NULL,
  516. &RegDataType,
  517. &RegData,
  518. &RegDataSize);
  519. if (err == ERROR_SUCCESS && RegDataSize == sizeof(BYTE) &&
  520. RegDataType == REG_BINARY && RegData != 0) {
  521. portType = PortTypeOther;
  522. break;
  523. }
  524. //
  525. // Check for a REG_BINARY (1-byte) 'PortSubClass' value entry set to 0.
  526. //
  527. RegDataSize = sizeof(RegData);
  528. if((ERROR_SUCCESS != RegQueryValueEx(hkDrv,
  529. m_szPortSubClass,
  530. NULL,
  531. &RegDataType,
  532. &RegData,
  533. &RegDataSize))
  534. || (RegDataSize != sizeof(BYTE))
  535. || (RegDataType != REG_BINARY))
  536. {
  537. portType = PortTypeSerial; // not a LPT/ECP device.
  538. }
  539. else {
  540. if (RegData == 0) {
  541. portType = PortTypeParallel;
  542. }
  543. else {
  544. portType = PortTypeSerial;
  545. }
  546. }
  547. } while (FALSE);
  548. if (hkDrv != 0) {
  549. RegCloseKey(hkDrv);
  550. hkDrv = 0;
  551. }
  552. if (hInf != INVALID_HANDLE_VALUE) {
  553. SetupCloseInfFile(hInf);
  554. hInf = INVALID_HANDLE_VALUE;
  555. }
  556. return portType;
  557. }
  558. // @@BEGIN_DDKSPLIT
  559. //
  560. // If the preferred value is available, let them have that one
  561. //
  562. VOID
  563. GenerateLptNumber(PDWORD Num,
  564. DWORD PreferredValue)
  565. {
  566. HKEY parallelMap;
  567. TCHAR valueName[40];
  568. TCHAR lptName[60], *lptNameLocation;
  569. int i = 0;
  570. DWORD valueSize, lptSize, regDataType, newLptNum;
  571. DWORD highestLptNum = 0;
  572. BOOL change = FALSE;
  573. TCHAR errorMsg[MAX_PATH];
  574. DWORD mask = 0;
  575. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  576. m_szRegParallelMap,
  577. 0,
  578. KEY_QUERY_VALUE ,
  579. &parallelMap) != ERROR_SUCCESS) {
  580. return;
  581. }
  582. valueSize = CharSizeOf(valueName);
  583. lptSize = sizeof(lptName);
  584. while (ERROR_SUCCESS == RegEnumValue(parallelMap,
  585. i++,
  586. valueName,
  587. &valueSize,
  588. NULL,
  589. &regDataType,
  590. (LPBYTE) lptName,
  591. &lptSize)) {
  592. if (regDataType == REG_SZ) {
  593. lptNameLocation = wcsstr(_wcsupr(lptName), m_szLPT);
  594. if (lptNameLocation) {
  595. newLptNum = myatoi(lptNameLocation + wcslen(m_szLPT));
  596. if (newLptNum == PreferredValue) {
  597. change = TRUE;
  598. }
  599. if (newLptNum > highestLptNum) {
  600. highestLptNum = newLptNum;
  601. }
  602. if (newLptNum <= PARALLEL_MAX_NUMBER && newLptNum > 0) {
  603. mask |= (1 << (newLptNum-1));
  604. }
  605. }
  606. }
  607. valueSize = CharSizeOf(valueName);
  608. lptSize = sizeof(lptName);
  609. }
  610. if (change) {
  611. if (mask < 7) {
  612. *Num = ((mask & 4)==0) ? PARALLEL_MAX_NUMBER : (((mask & 2)==0) ? 2 : 1);
  613. } else {
  614. *Num = highestLptNum + 1;
  615. }
  616. } else {
  617. *Num = PreferredValue;
  618. }
  619. RegCloseKey(parallelMap);
  620. }
  621. BOOL
  622. DetermineLptNumberFromResources(
  623. IN DEVINST DevInst,
  624. OUT PDWORD Num
  625. )
  626. /*++
  627. Routine Description:
  628. This routine retrieves the base IO port and IRQ for the specified device instance
  629. in a particular logconfig.
  630. Arguments:
  631. DevInst - Supplies the handle of a device instance to retrieve configuration for.
  632. Return Value:
  633. If success, the return value is TRUE, otherwise it is FALSE.
  634. --*/
  635. {
  636. LOG_CONF logConfig;
  637. RES_DES resDes;
  638. CONFIGRET cr;
  639. BOOL success;
  640. IO_RESOURCE ioResource;
  641. WORD base;
  642. ULONGLONG base2;
  643. if (CM_Get_First_Log_Conf(&logConfig,
  644. DevInst,
  645. BOOT_LOG_CONF) != CR_SUCCESS) {
  646. GenerateLptNumber(Num, PARALLEL_MAX_NUMBER);
  647. return TRUE;
  648. }
  649. success = FALSE; // assume failure.
  650. //
  651. // First, get the Io base port
  652. //
  653. if (CM_Get_Next_Res_Des(&resDes,
  654. logConfig,
  655. ResType_IO,
  656. NULL,
  657. 0) != CR_SUCCESS) {
  658. goto clean0;
  659. }
  660. cr = CM_Get_Res_Des_Data(resDes,
  661. &ioResource,
  662. sizeof(IO_RESOURCE),
  663. 0);
  664. CM_Free_Res_Des_Handle(resDes);
  665. if (cr != CR_SUCCESS) {
  666. goto clean0;
  667. }
  668. success = TRUE;
  669. //
  670. // Values for resources from ISA Architecture
  671. //
  672. base = (WORD) ioResource.IO_Header.IOD_Alloc_Base;
  673. if (IN_RANGE(base, 0x278, 0x27f)) {
  674. *Num = 2;
  675. }
  676. else if (IN_RANGE(base, 0x378, 0x37f)) {
  677. *Num = 1;
  678. }
  679. else if (base == 0x3bc) {
  680. *Num = 1;
  681. }
  682. else {
  683. //
  684. // Most machines only have one port anways, so just try that here
  685. //
  686. GenerateLptNumber(Num, PARALLEL_MAX_NUMBER);
  687. }
  688. clean0:
  689. CM_Free_Log_Conf_Handle(logConfig);
  690. return success;
  691. }
  692. // @@END_DDKSPLIT
  693. DWORD
  694. InstallPnPParallelPort(
  695. IN HDEVINFO DeviceInfoSet,
  696. IN PSP_DEVINFO_DATA DeviceInfoData
  697. )
  698. /*++
  699. // @@BEGIN_DDKSPLIT
  700. Routine Description:
  701. This routine installs a parallel (LPT or ECP) port.
  702. Arguments:
  703. DeviceInfoSet - Supplies a handle to the device information set containing
  704. the device being installed.
  705. DeviceInfoData - Supplies the address of the device information element
  706. being installed.
  707. Return Value:
  708. If successful, the return value is NO_ERROR, otherwise it is a Win32 error code.
  709. //
  710. // IGNORE the decription below, it is for the DDK only
  711. //
  712. // @@END_DDKSPLIT
  713. Routine Description:
  714. This routine installs a parallel port. In the DDK implementation, we let the
  715. default setup installer run and do nothing special.
  716. --*/
  717. {
  718. // @@BEGIN_DDKSPLIT
  719. TCHAR charBuffer[LINE_LEN],
  720. friendlyNameFormat[LINE_LEN],
  721. deviceDesc[LINE_LEN],
  722. lptPortName[20];
  723. PTCHAR lptLocation;
  724. DWORD lptPortNameSize, lptNum;
  725. HKEY hDeviceKey;
  726. TCHAR lpszService[MAX_PATH];
  727. DWORD error;
  728. //
  729. // We init the value here so that the DDK version of the function we have an
  730. // initialized value when it returns err. In the shipping version of this
  731. // function, we immediately set this to a different value.
  732. //
  733. // @@END_DDKSPLIT
  734. DWORD err = ERROR_DI_DO_DEFAULT;
  735. // @@BEGIN_DDKSPLIT
  736. err = ERROR_SUCCESS;
  737. //
  738. // Predispose the port name to 1. On almost any machine imaginable, there
  739. // will only be ONE LPT port, so we might as well assume it.
  740. //
  741. lptNum = PARALLEL_MAX_NUMBER;
  742. ZeroMemory(lptPortName, sizeof(lptPortName));
  743. //
  744. // First, make sure that Device Parameters\PortName exists and contains a
  745. // valid value so that when the parallel driver starts, it can name the
  746. // device
  747. //
  748. if ((hDeviceKey = SetupDiCreateDevRegKey(DeviceInfoSet,
  749. DeviceInfoData,
  750. DICS_FLAG_GLOBAL,
  751. 0,
  752. DIREG_DEV,
  753. NULL,
  754. NULL)) != INVALID_HANDLE_VALUE) {
  755. //
  756. // Retrieve the port name.
  757. //
  758. lptPortNameSize = sizeof(lptPortName);
  759. if (RegQueryValueEx(hDeviceKey,
  760. m_szPortName,
  761. NULL,
  762. NULL,
  763. (PBYTE)lptPortName,
  764. &lptPortNameSize) != ERROR_SUCCESS) {
  765. lptPortNameSize = sizeof(lptPortName);
  766. if (RegQueryValueEx(hDeviceKey,
  767. m_szDosDeviceName,
  768. NULL,
  769. NULL,
  770. (PBYTE) lptPortName,
  771. &lptPortNameSize) != ERROR_SUCCESS) {
  772. if (SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  773. DeviceInfoData,
  774. SPDRP_ENUMERATOR_NAME,
  775. NULL,
  776. (PBYTE)charBuffer,
  777. sizeof(charBuffer),
  778. NULL)) {
  779. if (lstrcmpi(charBuffer, m_szAcpiEnumName) == 0) {
  780. wsprintf(lptPortName, _T("%s%d"), m_szLPT, 1);
  781. }
  782. }
  783. if (*lptPortName != _T('\0')) {
  784. DWORD dwSize, dwFirmwareIdentified;
  785. dwSize = sizeof(dwFirmwareIdentified);
  786. if (RegQueryValueEx(hDeviceKey,
  787. m_szFirmwareIdentified,
  788. NULL,
  789. NULL,
  790. (PBYTE) &dwFirmwareIdentified,
  791. &dwSize) == ERROR_SUCCESS) {
  792. //
  793. // ACPI puts the value "FirmwareIdentified" if it has enumerated
  794. // this port. We only rely on this if a DDN isn't present and we
  795. // couldn't get the enumerator name
  796. //
  797. wsprintf(lptPortName, _T("%s%d"), m_szLPT, 1);
  798. }
  799. }
  800. }
  801. }
  802. if (lptPortName[0] != (TCHAR) 0) {
  803. _wcsupr(lptPortName);
  804. lptLocation = wcsstr(lptPortName, m_szLPT);
  805. if (lptLocation) {
  806. lptNum = myatoi(lptLocation + wcslen(m_szLPT));
  807. } else {
  808. DetermineLptNumberFromResources((DEVINST) DeviceInfoData->DevInst,
  809. &lptNum);
  810. }
  811. }
  812. else {
  813. DetermineLptNumberFromResources((DEVINST) DeviceInfoData->DevInst,
  814. &lptNum);
  815. }
  816. //
  817. // Check if this is a brand new port by querying the service value.
  818. // On a newly detected port, there will be no service value.
  819. //
  820. if (!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  821. DeviceInfoData,
  822. SPDRP_SERVICE,
  823. NULL,
  824. (LPBYTE) lpszService,
  825. MAX_PATH*sizeof(TCHAR),
  826. NULL)) {
  827. if (ERROR_INVALID_DATA == GetLastError())
  828. {
  829. GenerateLptNumber(&lptNum, lptNum);
  830. }
  831. }
  832. wsprintf(lptPortName, _T("LPT%d"), lptNum);
  833. //
  834. // If this fails, then we can't do much about it but continue
  835. //
  836. RegSetValueEx(hDeviceKey,
  837. m_szPortName,
  838. 0,
  839. REG_SZ,
  840. (PBYTE) lptPortName,
  841. ByteCountOf(lstrlen(lptPortName) + 1)
  842. );
  843. RegCloseKey(hDeviceKey);
  844. }
  845. //
  846. // Second, let the default installation take place.
  847. //
  848. if (!SetupDiInstallDevice(DeviceInfoSet, DeviceInfoData)) {
  849. return GetLastError();
  850. }
  851. //
  852. // Now generate a string, to be used for the device's friendly name, that incorporates
  853. // both the INF-specified device description, and the port name. For example,
  854. //
  855. // ECP Printer Port (LPT1)
  856. //
  857. if (LoadString(g_hInst,
  858. IDS_FRIENDLY_FORMAT,
  859. friendlyNameFormat,
  860. CharSizeOf(friendlyNameFormat)) &&
  861. SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  862. DeviceInfoData,
  863. SPDRP_DEVICEDESC,
  864. NULL,
  865. (PBYTE)deviceDesc,
  866. sizeof(deviceDesc),
  867. NULL)) {
  868. wsprintf(charBuffer, friendlyNameFormat, deviceDesc, lptPortName);
  869. }
  870. else {
  871. //
  872. // Simply use LPT port name.
  873. //
  874. lstrcpy(charBuffer, lptPortName);
  875. }
  876. SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
  877. DeviceInfoData,
  878. SPDRP_FRIENDLYNAME,
  879. (PBYTE)charBuffer,
  880. ByteCountOf(lstrlen(charBuffer) + 1)
  881. );
  882. //
  883. // Ignore the comments below, but KEEP THEM IN. We need them for the DDK
  884. //
  885. // @@END_DDKSPLIT
  886. //
  887. // Let the default setup installer install parallel ports for the DDK
  888. // version of this class installer
  889. //
  890. return err;
  891. }
  892. // @@BEGIN_DDKSPLIT
  893. DWORD
  894. GetDetectedSerialPortsList(
  895. IN HDEVINFO DeviceInfoSet,
  896. IN BOOL FirstTimeSetup
  897. )
  898. /*++
  899. Routine Description:
  900. This routine retrieves a list of all root-enumerated COM port device
  901. instances that are not manually installed (both phantoms and non-phantoms),
  902. and adds those device instances to the supplied device information set.
  903. See also ntos\io\pnpmap.c!PnPBiosEliminateDupes
  904. Arguments:
  905. DeviceInfoSet - Supplies a handle to the device information set into which
  906. the detected serial port elements are to be added.
  907. FirstTimeSetup - If non-zero, then we're in GUI-mode setup (responding to
  908. DIF_FIRSTTIMESETUP), and we only want to report (unregistered) devnodes
  909. created by the firmware mapper.
  910. Return Value:
  911. If successful, the return value is NO_ERROR, otherwise it is a Win32 error
  912. code indicating the cause of failure.
  913. --*/
  914. {
  915. CONFIGRET cr;
  916. PTCHAR DevIdBuffer;
  917. ULONG DevIdBufferLen, Status, Problem;
  918. PTSTR CurDevId, DeviceIdPart, p;
  919. DWORD i;
  920. DEVNODE DevNode;
  921. HWND hwndParent = NULL;
  922. SP_DEVINSTALL_PARAMS DeviceInstallParams;
  923. SP_DEVINFO_DATA DeviceInfoData;
  924. //
  925. // First retrieve a list of all root-enumerated device instances.
  926. //
  927. while(TRUE) {
  928. cr = CM_Get_Device_ID_List_Size(&DevIdBufferLen,
  929. m_szRootEnumName,
  930. CM_GETIDLIST_FILTER_ENUMERATOR
  931. );
  932. if((cr != CR_SUCCESS) || !DevIdBufferLen) {
  933. //
  934. // This should never happen.
  935. //
  936. return ERROR_INVALID_DATA;
  937. }
  938. if(!(DevIdBuffer = LocalAlloc(LPTR, DevIdBufferLen * sizeof(TCHAR)))) {
  939. return ERROR_NOT_ENOUGH_MEMORY;
  940. }
  941. cr = CM_Get_Device_ID_List(m_szRootEnumName,
  942. DevIdBuffer,
  943. DevIdBufferLen,
  944. CM_GETIDLIST_FILTER_ENUMERATOR
  945. );
  946. if(cr == CR_SUCCESS) {
  947. //
  948. // Device list retrieved successfully.
  949. //
  950. break;
  951. } else {
  952. //
  953. // Free the current buffer before determining what error occurred.
  954. //
  955. LocalFree(DevIdBuffer);
  956. //
  957. // If the error we encountered was anything other than buffer-too-
  958. // small, then we have to bail. (Note: since we sized our buffer
  959. // up-front, the only time we'll hit buffer-too-small is if someone
  960. // else is creating root-enumerated devnodes while we're trying to
  961. // retrieve the list.)
  962. //
  963. if(cr != CR_BUFFER_SMALL) {
  964. return ERROR_INVALID_DATA;
  965. }
  966. }
  967. }
  968. //
  969. // Retrieve the HWND associated with the device information set, so we can
  970. // specify that same handle for any device information elements we create.
  971. //
  972. DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
  973. if(SetupDiGetDeviceInstallParams(DeviceInfoSet, NULL, &DeviceInstallParams)) {
  974. hwndParent = DeviceInstallParams.hwndParent;
  975. }
  976. //
  977. // Now examine each device ID in our list, looking for ones that match up
  978. // with the list of serial IDs that the firmware mapper can report.
  979. //
  980. for(CurDevId = DevIdBuffer;
  981. *CurDevId;
  982. CurDevId += lstrlen(CurDevId) + 1) {
  983. //
  984. // Skip over the root-enumerator prefix plus the first backslash.
  985. //
  986. DeviceIdPart = CurDevId + (sizeof(m_szRootEnumName) / sizeof(TCHAR));
  987. //
  988. // Find the next backslash and temporarily replace it with a NULL char.
  989. //
  990. p = _tcschr(DeviceIdPart, TEXT('\\'));
  991. if (p)
  992. {
  993. *p = TEXT('\0');
  994. }
  995. for(i = 0; i < SERIAL_PNP_IDS_COUNT; i++) {
  996. if(!lstrcmpi(DeviceIdPart, m_pszSerialPnPIds[i])) {
  997. //
  998. // We found a match
  999. //
  1000. break;
  1001. }
  1002. }
  1003. //
  1004. // Before checking to see if we found a match, restore the backslash
  1005. //
  1006. if (p)
  1007. {
  1008. *p = TEXT('\\');
  1009. }
  1010. if(i >= SERIAL_PNP_IDS_COUNT) {
  1011. //
  1012. // We don't care about this device instance--move on to the next
  1013. // one.
  1014. //
  1015. continue;
  1016. }
  1017. //
  1018. // Next, attempt to locate the devnode (either present or not-present).
  1019. // Note that this call _will not_ succeed for device instances that are
  1020. // "private phantoms" (i.e., marked with the "Phantom" flag in their
  1021. // device instance key by the firmware mapper or by some other process
  1022. // that has created a new root-enumerated device instance, but has not
  1023. // yet registered it).
  1024. //
  1025. cr = CM_Locate_DevNode(&DevNode,
  1026. CurDevId,
  1027. CM_LOCATE_DEVINST_PHANTOM
  1028. );
  1029. if(cr == CR_SUCCESS) {
  1030. //
  1031. // We are dealing with a device that has been registered. It may
  1032. // or may not be present, however. Attempt to retrieve its status.
  1033. // If that fails, the device isn't present, and we don't want to
  1034. // return it in our list of detected serial ports. Also, we want
  1035. // to skip this device if it was manually installed.
  1036. //
  1037. // Also, make sure we're processing DIF_DETECT. We don't want to
  1038. // do this for DIF_FIRSTTIMESETUP, because GUI-mode setup doesn't
  1039. // pay attention to what previously-detected devices are no longer
  1040. // found, so all we end up doing is causing two installs for each
  1041. // detected device.
  1042. //
  1043. if(FirstTimeSetup
  1044. || (CR_SUCCESS != CM_Get_DevNode_Status(&Status,
  1045. &Problem,
  1046. DevNode,
  1047. 0))
  1048. || (Status & DN_MANUAL)) {
  1049. //
  1050. // Move on to the next device.
  1051. //
  1052. continue;
  1053. }
  1054. //
  1055. // OK, now we can add this device information element to our set of
  1056. // detected devices. Regardless of success or failure, we're done
  1057. // with this device--it's time to move on to the next one.
  1058. //
  1059. SetupDiOpenDeviceInfo(DeviceInfoSet,
  1060. CurDevId,
  1061. hwndParent,
  1062. 0,
  1063. NULL
  1064. );
  1065. continue;
  1066. }
  1067. //
  1068. // If we get to here, then we've found a private phantom. Create a
  1069. // device information element for this device. The underlying code
  1070. // that implements CM_Create_DevInst won't allow creation of a device
  1071. // instance that's already a private phantom _unless_ that device
  1072. // instance was created by the firmware mapper. Thus, we don't have to
  1073. // worry about the (admittedly unlikely) case that we caught a private
  1074. // phantom created by someone else (e.g., another detection in
  1075. // progress.)
  1076. //
  1077. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1078. if(!SetupDiCreateDeviceInfo(DeviceInfoSet,
  1079. CurDevId,
  1080. &GUID_DEVCLASS_PORTS,
  1081. NULL,
  1082. hwndParent,
  1083. 0,
  1084. &DeviceInfoData)) {
  1085. //
  1086. // We were unable to create a device information element for this
  1087. // private phantom (maybe because it wasn't a creation of the
  1088. // firmware mapper). At any rate, there's nothing we can do, so
  1089. // skip this device and continue on.
  1090. //
  1091. continue;
  1092. }
  1093. //
  1094. // OK, we have a device information element for our detected serial
  1095. // port. DIF_FIRSTTIMESETUP expects us to have a driver selected for
  1096. // any devices we return. DIF_DETECT doesn't make this requirement,
  1097. // but it does respect the driver selection, if we make one. Thus, we
  1098. // always go ahead and do the compatible driver search ourselves.
  1099. //
  1100. if(!SetupDiBuildDriverInfoList(DeviceInfoSet,
  1101. &DeviceInfoData,
  1102. SPDIT_COMPATDRIVER)) {
  1103. //
  1104. // This should never fail--if it does, bail and move on to the next
  1105. // device.
  1106. //
  1107. SetupDiDeleteDeviceInfo(DeviceInfoSet, &DeviceInfoData);
  1108. continue;
  1109. }
  1110. //
  1111. // Now select the best driver from among the compatible matches for the
  1112. // device.
  1113. //
  1114. if(!SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV,
  1115. DeviceInfoSet,
  1116. &DeviceInfoData)) {
  1117. //
  1118. // This shouldn't fail, unless something really bad has happened
  1119. // such as the user deleting %windir%\Inf\msports.inf. If that
  1120. // happens, then once again we've no choice but to bail and move on
  1121. // to the next device.
  1122. //
  1123. SetupDiDeleteDeviceInfo(DeviceInfoSet, &DeviceInfoData);
  1124. continue;
  1125. }
  1126. //
  1127. // We've successfully added the detected device to the device info set.
  1128. // On to the next device...
  1129. //
  1130. }
  1131. LocalFree(DevIdBuffer);
  1132. return NO_ERROR;
  1133. }
  1134. DWORD
  1135. RegisterDetectedSerialPort(
  1136. IN HDEVINFO DeviceInfoSet,
  1137. IN PSP_DEVINFO_DATA DeviceInfoData
  1138. )
  1139. /*++
  1140. Routine Description:
  1141. This routine performs duplicate detection on the specified device
  1142. information element, and if it isn't found to be a duplicate of any
  1143. existing device, this devinfo element is registered (thus transforming it
  1144. from just registry spooge into a real, live root-enumerated devnode).
  1145. Arguments:
  1146. DeviceInfoSet - Supplies the handle of the device information set that
  1147. contains the element to be registered.
  1148. DeviceInfoData - Supplies the context structure for the device information
  1149. element to be registered.
  1150. Return Value:
  1151. If the device isn't a duplicate, the return value is NO_ERROR.
  1152. Otherwise, it is some other Win32 error code indicating the cause of
  1153. failure. The most common failure is due to having detected the device as
  1154. being a duplicate of an existing one--in that case the error reported is
  1155. ERROR_DUPLICATE_FOUND.
  1156. Remarks:
  1157. If the device being registered wasn't created via device detection (i.e.,
  1158. it doesn't have a boot config), then we just return ERROR_DI_DO_DEFAULT.
  1159. --*/
  1160. {
  1161. CONFIGRET cr;
  1162. LOG_CONF LogConf;
  1163. RES_DES ResDes;
  1164. IO_RESOURCE IoResource;
  1165. CONFLICT_LIST ConflictList;
  1166. ULONG ConflictCount, ConflictIndex;
  1167. CONFLICT_DETAILS ConflictDetails;
  1168. INT i, PosDupIndex, PosDupCount;
  1169. PTCHAR IdBuffer = NULL;
  1170. ULONG IdBufferSize = 0;
  1171. DWORD Err;
  1172. PCTSTR p;
  1173. PTCHAR SerialDevNodeList = NULL;
  1174. ULONG SerialDevNodeListSize;
  1175. TCHAR CharBuffer[MAX_DEVNODE_ID_LEN];
  1176. ULONG CharBufferSize;
  1177. PTSTR *PosDupList;
  1178. //
  1179. // First, check to see if the boot config for this device conflicts with
  1180. // any other device. If it doesn't, then we know we don't have a duplicate.
  1181. //
  1182. if(!GetSerialPortDevInstConfig((DEVNODE)(DeviceInfoData->DevInst),
  1183. BOOT_LOG_CONF,
  1184. &IoResource,
  1185. NULL)) {
  1186. //
  1187. // The device instance doesn't have a boot config--this will happen if
  1188. // the user is attempting to manually install a COM port (i.e., not via
  1189. // detection). In this case, just let the default behavior happen.
  1190. //
  1191. return ERROR_DI_DO_DEFAULT;
  1192. }
  1193. //
  1194. // We can't query for the resource conflict list on a phantom devnode.
  1195. // Therefore, we are forced to register this devnode now, then uninstall it
  1196. // later if we discover that it is, in fact, a duplicate.
  1197. //
  1198. if(!SetupDiRegisterDeviceInfo(DeviceInfoSet,
  1199. DeviceInfoData,
  1200. 0,
  1201. NULL,
  1202. NULL,
  1203. NULL)) {
  1204. //
  1205. // Device couldn't be registered.
  1206. //
  1207. return GetLastError();
  1208. }
  1209. cr = CM_Query_Resource_Conflict_List(&ConflictList,
  1210. (DEVNODE)(DeviceInfoData->DevInst),
  1211. ResType_IO,
  1212. &IoResource,
  1213. sizeof(IoResource),
  1214. 0,
  1215. NULL
  1216. );
  1217. if(cr != CR_SUCCESS) {
  1218. //
  1219. // Couldn't retrieve a conflict list--assume there are no conflicts,
  1220. // thus this device isn't a duplicate.
  1221. //
  1222. return NO_ERROR;
  1223. }
  1224. //
  1225. // Find out how many things conflicted.
  1226. //
  1227. if((CR_SUCCESS != CM_Get_Resource_Conflict_Count(ConflictList, &ConflictCount))
  1228. || !ConflictCount) {
  1229. //
  1230. // Either we couldn't retrieve the conflict count, or it was zero. In
  1231. // any case, we should assume this device isn't a duplicate.
  1232. //
  1233. Err = NO_ERROR;
  1234. goto clean1;
  1235. }
  1236. //
  1237. // Retrieve the list of devnodes with which the Serial service is
  1238. // associated (as either the function driver or a filter driver).
  1239. //
  1240. SerialDevNodeListSize = 1024; // start out with a 1K character buffer
  1241. while(TRUE) {
  1242. if(!(SerialDevNodeList = LocalAlloc(LPTR, SerialDevNodeListSize))) {
  1243. //
  1244. // Out of memory--time to bail!
  1245. //
  1246. Err = ERROR_NOT_ENOUGH_MEMORY;
  1247. goto clean1;
  1248. }
  1249. cr = CM_Get_Device_ID_List(m_szSERIAL,
  1250. SerialDevNodeList,
  1251. SerialDevNodeListSize,
  1252. CM_GETIDLIST_FILTER_SERVICE
  1253. );
  1254. if(cr == CR_SUCCESS) {
  1255. break;
  1256. }
  1257. LocalFree(SerialDevNodeList);
  1258. SerialDevNodeList = NULL;
  1259. if(cr != CR_BUFFER_SMALL) {
  1260. //
  1261. // We failed for some reason other than buffer-too-small. Maybe
  1262. // the Serial service isn't even installed. At any rate, we'll
  1263. // just skip this part of our check when processing the conflicting
  1264. // devnodes below.
  1265. //
  1266. break;
  1267. }
  1268. //
  1269. // Figure out how big of a buffer we actually need,
  1270. //
  1271. cr = CM_Get_Device_ID_List_Size(&SerialDevNodeListSize,
  1272. m_szSERIAL,
  1273. CM_GETIDLIST_FILTER_SERVICE
  1274. );
  1275. if(cr != CR_SUCCESS) {
  1276. //
  1277. // This shouldn't fail, but if it does we'll just do without the
  1278. // list.
  1279. //
  1280. break;
  1281. }
  1282. }
  1283. //
  1284. // Retrieve the list of possible duplicate IDs
  1285. //
  1286. if(!GetPosDupList(DeviceInfoSet, DeviceInfoData, &PosDupList, &PosDupCount)) {
  1287. //
  1288. // We couldn't retrieve the PosDup list for some reason--default to
  1289. // the list of IDs known to be spat out by the firmware mapper.
  1290. //
  1291. PosDupList = m_pszSerialPnPIds;
  1292. PosDupCount = SERIAL_PNP_IDS_COUNT;
  1293. }
  1294. //
  1295. // Loop through each conflict, checking to see whether our device is a
  1296. // duplicate of any of them.
  1297. //
  1298. for(ConflictIndex = 0; ConflictIndex < ConflictCount; ConflictIndex++) {
  1299. ZeroMemory(&ConflictDetails, sizeof(ConflictDetails));
  1300. ConflictDetails.CD_ulSize = sizeof(CONFLICT_DETAILS);
  1301. ConflictDetails.CD_ulMask = CM_CDMASK_DEVINST | CM_CDMASK_FLAGS;
  1302. cr = CM_Get_Resource_Conflict_Details(ConflictList,
  1303. ConflictIndex,
  1304. &ConflictDetails
  1305. );
  1306. //
  1307. // If we failed to retrieve the conflict details, or if the conflict
  1308. // was not with a PnP devnode, then we can ignore this conflict.
  1309. //
  1310. if((cr != CR_SUCCESS)
  1311. || (ConflictDetails.CD_dnDevInst == -1)
  1312. || (ConflictDetails.CD_ulFlags & (CM_CDFLAGS_DRIVER
  1313. | CM_CDFLAGS_ROOT_OWNED
  1314. | CM_CDFLAGS_RESERVED))) {
  1315. continue;
  1316. }
  1317. //
  1318. // We have a devnode--first check to see if this is the HAL devnode
  1319. // (class = "Computer"). If so, then we've found the serial port in
  1320. // use by the kernel debugger.
  1321. //
  1322. CharBufferSize = sizeof(CharBuffer);
  1323. cr = CM_Get_DevNode_Registry_Property(ConflictDetails.CD_dnDevInst,
  1324. CM_DRP_CLASSGUID,
  1325. NULL,
  1326. CharBuffer,
  1327. &CharBufferSize,
  1328. 0
  1329. );
  1330. if((cr == CR_SUCCESS) && !lstrcmpi(CharBuffer, m_szComputerClassGuidString)) {
  1331. //
  1332. // We're conflicting with the HAL, presumably because it's claimed
  1333. // the serial port IO addresses for use as the kernel debugger port.
  1334. //
  1335. // There are 3 scenarios:
  1336. //
  1337. // 1. non-ACPI, non-PnPBIOS machine -- detection is not required
  1338. // on these machines, because the mapper-reported devnodes are
  1339. // not reported as phantoms in the first place.
  1340. //
  1341. // 2. PnPBIOS or ACPI machine, debugger on PnP COM port -- we
  1342. // don't want to install our detected devnode because it's a
  1343. // duplicate.
  1344. //
  1345. // 3. PnPBIOS or ACPI machine, debugger on legacy COM port -- we
  1346. // _should_ install this devnode, because otherwise having the
  1347. // kernel debugger hooked up will prevent us from detecting
  1348. // the COM port.
  1349. //
  1350. // Unfortunately, we can't distinguish between cases (2) and (3) on
  1351. // ACPI machines, because ACPI doesn't enumerate a devnode for the
  1352. // serial port that's being used as the kernel debugger. For now,
  1353. // we're going to punt case (3) and say "tough"--you have to
  1354. // disable the kernel debugger, reboot and re-run the hardware
  1355. // wizard. This isn't too bad considering that it's no worse than
  1356. // what would happen if we actually had to poke at ports to detect
  1357. // the COM port. In that case, too, we would be unable to detect
  1358. // the COM port if it was already in use by the debugger.
  1359. //
  1360. Err = ERROR_DUPLICATE_FOUND;
  1361. goto clean2;
  1362. }
  1363. //
  1364. // OK, we're not looking at the kernel debugger port. Now check to see
  1365. // if one of our known mapper-reported IDs is among this device's list
  1366. // of hardware or compatible IDs.
  1367. //
  1368. for(i = 0; i < 2; i++) {
  1369. cr = CM_Get_DevNode_Registry_Property(ConflictDetails.CD_dnDevInst,
  1370. (i ? CM_DRP_COMPATIBLEIDS
  1371. : CM_DRP_HARDWAREID),
  1372. NULL,
  1373. IdBuffer,
  1374. &IdBufferSize,
  1375. 0
  1376. );
  1377. if(cr == CR_BUFFER_SMALL) {
  1378. if(IdBuffer) {
  1379. LocalFree(IdBuffer);
  1380. }
  1381. if(!(IdBuffer = LocalAlloc(LPTR, IdBufferSize))) {
  1382. //
  1383. // Out of memory--time to bail!
  1384. //
  1385. Err = ERROR_NOT_ENOUGH_MEMORY;
  1386. goto clean2;
  1387. }
  1388. //
  1389. // Decrement our index, so when we loop around again, we'll
  1390. // re-attempt to retrieve the same property.
  1391. //
  1392. i--;
  1393. continue;
  1394. } else if(cr != CR_SUCCESS) {
  1395. //
  1396. // Failed to retrieve the property--just move on to the next
  1397. // one.
  1398. //
  1399. continue;
  1400. }
  1401. //
  1402. // If we get to here, we successfully retrieved a multi-sz list of
  1403. // hardware or compatible IDs for this device.
  1404. //
  1405. for(p = IdBuffer; *p; p += (lstrlen(p) + 1)) {
  1406. for(PosDupIndex = 0; PosDupIndex < PosDupCount; PosDupIndex++) {
  1407. if(!lstrcmpi(p, PosDupList[PosDupIndex])) {
  1408. //
  1409. // We found a match--our guy's a dupe.
  1410. //
  1411. Err = ERROR_DUPLICATE_FOUND;
  1412. goto clean2;
  1413. }
  1414. }
  1415. }
  1416. }
  1417. //
  1418. // If we get to here, then we didn't find any duplicates based on ID
  1419. // matching. However, there are some 16550-compatible PnP devices that
  1420. // don't report the correct compatible ID. However, we have another
  1421. // trick we can use--if the device has serial.sys as either the
  1422. // function driver or a filter driver, then this is a solid indicator
  1423. // that we have a dupe.
  1424. //
  1425. if(SerialDevNodeList) {
  1426. //
  1427. // Retrieve the name of this devnode so we can compare it against
  1428. // the list of devnodes with which the Serial service is associated.
  1429. //
  1430. if(CR_SUCCESS == CM_Get_Device_ID(ConflictDetails.CD_dnDevInst,
  1431. CharBuffer,
  1432. sizeof(CharBuffer) / sizeof(TCHAR),
  1433. 0)) {
  1434. for(p = SerialDevNodeList; *p; p += (lstrlen(p) + 1)) {
  1435. if(!lstrcmpi(CharBuffer, p)) {
  1436. //
  1437. // This devnode is using serial.sys--it must be a dupe.
  1438. //
  1439. Err = ERROR_DUPLICATE_FOUND;
  1440. goto clean2;
  1441. }
  1442. }
  1443. }
  1444. }
  1445. }
  1446. //
  1447. // If we get here, then all our checks have past--our newly-detected device
  1448. // instance is not a duplicate of any other existing devnodes.
  1449. //
  1450. Err = NO_ERROR;
  1451. clean2:
  1452. if(SerialDevNodeList) {
  1453. LocalFree(SerialDevNodeList);
  1454. }
  1455. if(IdBuffer) {
  1456. LocalFree(IdBuffer);
  1457. }
  1458. if(PosDupList != m_pszSerialPnPIds) {
  1459. for(PosDupIndex = 0; PosDupIndex < PosDupCount; PosDupIndex++) {
  1460. LocalFree(PosDupList[PosDupIndex]);
  1461. }
  1462. LocalFree(PosDupList);
  1463. }
  1464. clean1:
  1465. CM_Free_Resource_Conflict_Handle(ConflictList);
  1466. if(Err != NO_ERROR) {
  1467. //
  1468. // Since we registered the devnode, we must manually uninstall it if
  1469. // we fail.
  1470. //
  1471. SetupDiRemoveDevice(DeviceInfoSet, DeviceInfoData);
  1472. }
  1473. return Err;
  1474. }
  1475. BOOL
  1476. GetPosDupList(
  1477. IN HDEVINFO DeviceInfoSet,
  1478. IN PSP_DEVINFO_DATA DeviceInfoData,
  1479. OUT PTSTR **PosDupList,
  1480. OUT INT *PosDupCount
  1481. )
  1482. /*++
  1483. Routine Description:
  1484. This routine retrieves the list of PosDup IDs contained in the
  1485. [<ActualInstallSec>.PosDup] INF section for the device information
  1486. element's selected driver node.
  1487. Arguments:
  1488. DeviceInfoSet - Supplies the handle of the device information set that
  1489. contains the device information element for which a driver is selected
  1490. DeviceInfoData - Supplies the context structure for the device information
  1491. element for which a driver node is selected. The PosDup list will be
  1492. retrieved based on this driver node's (potentially decorated) INF
  1493. install section.
  1494. PosDupList - Supplies the address of a pointer that will be set, upon
  1495. successful return, to point to a newly-allocated array of string
  1496. pointers, each pointing to a newly-allocated string buffer containing
  1497. a device ID referenced in the relevant PosDup section for the selected
  1498. driver node.
  1499. PosDupCount - Supplies the address of an integer variable that, upon
  1500. successful return, receives the number of string pointers stored in the
  1501. PosDupList array.
  1502. Return Value:
  1503. If successful, the return value is non-zero. The caller is responsible for
  1504. freeing each string pointer in the array, as well as the array buffer
  1505. itself.
  1506. If unsuccessful, the return value is zero (FALSE). (Note: the call is also
  1507. considered unsuccessful if there's no associated PosDup section, or if it's
  1508. empty).
  1509. --*/
  1510. {
  1511. SP_DRVINFO_DATA DriverInfoData;
  1512. SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
  1513. HINF hInf;
  1514. TCHAR InfSectionWithExt[255]; // MAX_SECT_NAME_LEN from setupapi\inf.h
  1515. BOOL b = FALSE;
  1516. LONG LineCount, LineIndex;
  1517. INFCONTEXT InfContext;
  1518. DWORD NumElements, NumFields, FieldIndex;
  1519. TCHAR PosDupId[MAX_DEVICE_ID_LEN];
  1520. PTSTR PosDupCopy;
  1521. //
  1522. // Get the driver node selected for the specified device information
  1523. // element.
  1524. //
  1525. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  1526. if(!SetupDiGetSelectedDriver(DeviceInfoSet, DeviceInfoData, &DriverInfoData)) {
  1527. //
  1528. // No driver node selected--there's nothing we can do!
  1529. //
  1530. goto clean0;
  1531. }
  1532. //
  1533. // Now retrieve the corresponding INF and install section.
  1534. //
  1535. DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
  1536. if(!SetupDiGetDriverInfoDetail(DeviceInfoSet,
  1537. DeviceInfoData,
  1538. &DriverInfoData,
  1539. &DriverInfoDetailData,
  1540. sizeof(DriverInfoDetailData),
  1541. NULL)
  1542. && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
  1543. //
  1544. // We failed, and it wasn't because the buffer was too small. We gotta
  1545. // bail.
  1546. //
  1547. goto clean0;
  1548. }
  1549. //
  1550. // Open the INF for this driver node.
  1551. //
  1552. hInf = SetupOpenInfFile(DriverInfoDetailData.InfFileName,
  1553. NULL,
  1554. INF_STYLE_WIN4,
  1555. NULL
  1556. );
  1557. if(hInf == INVALID_HANDLE_VALUE) {
  1558. goto clean0;
  1559. }
  1560. //
  1561. // Get the (potentially decorated) install section name.
  1562. //
  1563. if(!SetupDiGetActualSectionToInstall(hInf,
  1564. DriverInfoDetailData.SectionName,
  1565. InfSectionWithExt,
  1566. sizeof(InfSectionWithExt) / sizeof(TCHAR),
  1567. NULL,
  1568. NULL)) {
  1569. goto clean1;
  1570. }
  1571. //
  1572. // Append ".PosDup" to decorated install section.
  1573. //
  1574. lstrcat(InfSectionWithExt, m_szPosDupSectionSuffix);
  1575. //
  1576. // First, figure out the size of the array we're going to populate...
  1577. //
  1578. NumElements = 0;
  1579. //
  1580. // Loop through each line in the PosDup section.
  1581. //
  1582. LineCount = SetupGetLineCount(hInf, InfSectionWithExt);
  1583. for(LineIndex = 0; LineIndex < LineCount; LineIndex++) {
  1584. if(SetupGetLineByIndex(hInf, InfSectionWithExt, LineIndex, &InfContext)) {
  1585. NumElements += SetupGetFieldCount(&InfContext);
  1586. }
  1587. }
  1588. if(!NumElements) {
  1589. //
  1590. // We didn't find any PosDup entries.
  1591. //
  1592. goto clean1;
  1593. }
  1594. //
  1595. // Now allocate a buffer big enough to hold all these entries.
  1596. //
  1597. *PosDupList = LocalAlloc(LPTR, NumElements * sizeof(PTSTR));
  1598. if(!*PosDupList) {
  1599. goto clean1;
  1600. }
  1601. *PosDupCount = 0;
  1602. //
  1603. // Now loop though each PosDup entry, and store copies of those entries in
  1604. // our array.
  1605. //
  1606. for(LineIndex = 0; LineIndex < LineCount; LineIndex++) {
  1607. if(SetupGetLineByIndex(hInf, InfSectionWithExt, LineIndex, &InfContext)) {
  1608. NumFields = SetupGetFieldCount(&InfContext);
  1609. for(FieldIndex = 1; FieldIndex <= NumFields; FieldIndex++) {
  1610. if(!SetupGetStringField(&InfContext,
  1611. FieldIndex,
  1612. PosDupId,
  1613. sizeof(PosDupId) / sizeof(TCHAR),
  1614. NULL)) {
  1615. //
  1616. // This shouldn't fail, but if it does, just move on to the
  1617. // next field.
  1618. //
  1619. continue;
  1620. }
  1621. PosDupCopy = LocalAlloc(LPTR,
  1622. (lstrlen(PosDupId) + 1) * sizeof(TCHAR)
  1623. );
  1624. if(!PosDupCopy) {
  1625. goto clean2;
  1626. }
  1627. lstrcpy(PosDupCopy, PosDupId);
  1628. (*PosDupList)[(*PosDupCount)++] = PosDupCopy;
  1629. }
  1630. }
  1631. }
  1632. //
  1633. // If we get to here, and we found even one PosDup entry, consider the
  1634. // operation a success
  1635. //
  1636. if(*PosDupCount) {
  1637. b = TRUE;
  1638. goto clean1;
  1639. }
  1640. clean2:
  1641. //
  1642. // Something bad happened--clean up all memory allocated.
  1643. //
  1644. {
  1645. INT i;
  1646. for(i = 0; i < *PosDupCount; i++) {
  1647. LocalFree((*PosDupList)[i]);
  1648. }
  1649. LocalFree(*PosDupList);
  1650. }
  1651. clean1:
  1652. SetupCloseInfFile(hInf);
  1653. clean0:
  1654. return b;
  1655. }
  1656. // @@END_DDKSPLIT
  1657. #define NO_COM_NUMBER 0
  1658. BOOL
  1659. DetermineComNumberFromResources(
  1660. IN DEVINST DevInst,
  1661. OUT PDWORD Num
  1662. )
  1663. /*++
  1664. Routine Description:
  1665. This routine retrieves the base IO port and IRQ for the specified device instance
  1666. in a particular logconfig.
  1667. If a successful match is found, then *Num == found number, otherwise
  1668. *Num == NO_COM_NUMBER.
  1669. Arguments:
  1670. DevInst - Supplies the handle of a device instance to retrieve configuration for.
  1671. Return Value:
  1672. If success, the return value is TRUE, otherwise it is FALSE.
  1673. --*/
  1674. {
  1675. LOG_CONF logConfig;
  1676. RES_DES resDes;
  1677. CONFIGRET cr;
  1678. BOOL success;
  1679. IO_RESOURCE ioResource;
  1680. WORD base;
  1681. ULONGLONG base2;
  1682. success = FALSE; // assume failure.
  1683. *Num = NO_COM_NUMBER;
  1684. //
  1685. // If the device does not have a boot config, use the com db
  1686. //
  1687. if (CM_Get_First_Log_Conf(&logConfig,
  1688. DevInst,
  1689. BOOT_LOG_CONF) != CR_SUCCESS) {
  1690. return success;
  1691. }
  1692. //
  1693. // First, get the Io base port
  1694. //
  1695. if (CM_Get_Next_Res_Des(&resDes,
  1696. logConfig,
  1697. ResType_IO,
  1698. NULL,
  1699. 0) != CR_SUCCESS) {
  1700. goto clean0;
  1701. }
  1702. cr = CM_Get_Res_Des_Data(resDes,
  1703. &ioResource,
  1704. sizeof(IO_RESOURCE),
  1705. 0);
  1706. CM_Free_Res_Des_Handle(resDes);
  1707. if (cr != CR_SUCCESS) {
  1708. goto clean0;
  1709. }
  1710. //
  1711. // Values for resources from ISA Architecture
  1712. //
  1713. base = (WORD) ioResource.IO_Header.IOD_Alloc_Base;
  1714. if (IN_RANGE(base, 0x3f8, 0x3ff)) {
  1715. *Num = 1;
  1716. }
  1717. else if (IN_RANGE(base, 0x2f8, 0x2ff)) {
  1718. *Num = 2;
  1719. }
  1720. else if (IN_RANGE(base, 0x3e8, 0x3ef)) {
  1721. *Num = 3;
  1722. }
  1723. else if (IN_RANGE(base, 0x2e8, 0x2ef)) {
  1724. *Num = 4;
  1725. }
  1726. if (*Num != NO_COM_NUMBER) {
  1727. success = TRUE;
  1728. }
  1729. clean0:
  1730. CM_Free_Log_Conf_Handle(logConfig);
  1731. return success;
  1732. }
  1733. #define DEF_MIN_COM_NUM (5)
  1734. DWORD
  1735. InstallPnPSerialPort(
  1736. IN HDEVINFO DeviceInfoSet,
  1737. IN PSP_DEVINFO_DATA DeviceInfoData
  1738. )
  1739. /*++
  1740. Routine Description:
  1741. This routine performs the installation of a PnP ISA serial port device (may
  1742. actually be a modem card). This involves the following steps:
  1743. 1. Select a COM port number and serial device name for this port
  1744. (This involves duplicate detection, since PnP ISA cards will
  1745. sometimes have a boot config, and thus be reported by ntdetect/ARC
  1746. firmware.)
  1747. 2. Create a subkey under the serial driver's Parameters key, and
  1748. set it up just as if it was a manually-installed port.
  1749. 3. Display the resource selection dialog, and allow the user to
  1750. configure the settings for the port.
  1751. 4. Write out the settings to the serial port's key in legacy format
  1752. (i.e., the way serial.sys expects to see it).
  1753. 5. Write out PnPDeviceId value to the serial port's key, which gives
  1754. the device instance name with which this port is associated.
  1755. 6. Write out PortName value to the devnode key, so that modem class
  1756. installer can continue with installation (if this is really a
  1757. PnP ISA modem).
  1758. Arguments:
  1759. DeviceInfoSet - Supplies a handle to the device information set containing
  1760. the device being installed.
  1761. DeviceInfoData - Supplies the address of the device information element
  1762. being installed.
  1763. Return Value:
  1764. If successful, the return value is NO_ERROR, otherwise it is a Win32 error code.
  1765. --*/
  1766. {
  1767. HKEY hKey;
  1768. HCOMDB hComDB;
  1769. TCHAR comPort[40],
  1770. szPortName[20],
  1771. charBuffer[MAX_PATH],
  1772. friendlyNameFormat[LINE_LEN],
  1773. deviceDesc[LINE_LEN];
  1774. PTCHAR comLocation;
  1775. DWORD comPortSize1,comPortSize2,
  1776. comPortNumber = NO_COM_NUMBER,
  1777. portsReported;
  1778. DWORD dwFirmwareIdentified, dwSize;
  1779. BYTE portUsage[32];
  1780. BOOL res;
  1781. DWORD firmwarePort = FALSE;
  1782. #if MAX_DEVICE_ID_LEN > MAX_PATH
  1783. #error MAX_DEVICE_ID_LEN is greater than MAX_PATH. Update charBuffer.
  1784. #endif
  1785. ZeroMemory(comPort, sizeof(comPort));
  1786. ComDBOpen(&hComDB);
  1787. if ((hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
  1788. DeviceInfoData,
  1789. DICS_FLAG_GLOBAL,
  1790. 0,
  1791. DIREG_DEV,
  1792. KEY_READ)) != INVALID_HANDLE_VALUE) {
  1793. comPortSize1 = sizeof(comPort);
  1794. comPortSize2 = sizeof(comPort);
  1795. if (RegQueryValueEx(hKey,
  1796. m_szPortName,
  1797. NULL,
  1798. NULL,
  1799. (PBYTE)comPort,
  1800. &comPortSize1) == ERROR_SUCCESS) {
  1801. firmwarePort = TRUE;
  1802. }
  1803. else if (RegQueryValueEx(hKey,
  1804. m_szDosDeviceName,
  1805. NULL,
  1806. NULL,
  1807. (PBYTE) comPort,
  1808. &comPortSize2) == ERROR_SUCCESS) {
  1809. //
  1810. // ACPI puts the name of the port as DosDeviceName, use this name
  1811. // as the basis for what to call this port
  1812. //
  1813. firmwarePort = TRUE;
  1814. }
  1815. else {
  1816. //
  1817. // Our final check is to check the enumerator. We care about two
  1818. // cases:
  1819. //
  1820. // 1) If the enumerators is ACPI. If so, blindly consider this
  1821. // a firmware port (and get the BIOS mfg to provide a _DDN method
  1822. // for this device!)
  1823. //
  1824. // 2) The port is "root" enumerated, yet it's not marked as
  1825. // DN_ROOT_ENUMERATED. This is the
  1826. // way we distinguish PnPBIOS-reported devnodes. Note that, in
  1827. // general, these devnodes would've been caught by the check for a
  1828. // "PortName" value above, but this won't be present if we couldn't
  1829. // find a matching ntdetect-reported device from which to migrate
  1830. // the COM port name.
  1831. //
  1832. // Note also that this check doesn't catch ntdetect or firmware
  1833. // reported devices. In these cases, we should already have a
  1834. // PortName, thus the check above should catch those devices. In
  1835. // the unlikely event that we encounter an ntdetect or firmware
  1836. // devnode that doesn't already have a COM port name, then it'll
  1837. // get an arbitrary one assigned. Oh well.
  1838. //
  1839. if (SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  1840. DeviceInfoData,
  1841. SPDRP_ENUMERATOR_NAME,
  1842. NULL,
  1843. (PBYTE)charBuffer,
  1844. sizeof(charBuffer),
  1845. NULL)) {
  1846. if (lstrcmpi(charBuffer, m_szAcpiEnumName) == 0) {
  1847. firmwarePort = TRUE;
  1848. }
  1849. else if (lstrcmpi(charBuffer, m_szRootEnumName) == 0) {
  1850. ULONG status, problem;
  1851. if ((CM_Get_DevNode_Status(&status,
  1852. &problem,
  1853. (DEVNODE)(DeviceInfoData->DevInst),
  1854. 0) == CR_SUCCESS)
  1855. && !(status & DN_ROOT_ENUMERATED))
  1856. {
  1857. firmwarePort = TRUE;
  1858. }
  1859. }
  1860. }
  1861. dwSize = sizeof(dwFirmwareIdentified);
  1862. if (firmwarePort == FALSE &&
  1863. RegQueryValueEx(hKey,
  1864. m_szFirmwareIdentified,
  1865. NULL,
  1866. NULL,
  1867. (PBYTE) &dwFirmwareIdentified,
  1868. &dwSize) == ERROR_SUCCESS) {
  1869. //
  1870. // ACPI puts the value "FirmwareIdentified" if it has enumerated
  1871. // this port. We only rely on this if a DDN isn't present and we
  1872. // couldn't get the enumerator name
  1873. //
  1874. firmwarePort = TRUE;
  1875. }
  1876. ZeroMemory(charBuffer, sizeof(charBuffer));
  1877. }
  1878. RegCloseKey(hKey);
  1879. }
  1880. if (firmwarePort) {
  1881. //
  1882. // Try to find "COM" in the name. If it is found, simply extract
  1883. // the number that follows it and use that as the com number.
  1884. //
  1885. // Otherwise:
  1886. // 1) try to determine the number of the com port based on its
  1887. // IO range, otherwise
  1888. // 2) look through the com db and try to find an unused port from
  1889. // 1 to 4, if none are present then let the DB pick the next open
  1890. // port number
  1891. //
  1892. if (comPort[0] != (TCHAR) 0) {
  1893. _wcsupr(comPort);
  1894. comLocation = wcsstr(comPort, m_szCOM);
  1895. if (comLocation) {
  1896. comPortNumber = myatoi(comLocation + wcslen(m_szCOM));
  1897. }
  1898. }
  1899. if (comPortNumber == NO_COM_NUMBER &&
  1900. !DetermineComNumberFromResources((DEVINST) DeviceInfoData->DevInst,
  1901. &comPortNumber) &&
  1902. (hComDB != HCOMDB_INVALID_HANDLE_VALUE) &&
  1903. (ComDBGetCurrentPortUsage(hComDB,
  1904. portUsage,
  1905. MAX_COM_PORT / 8,
  1906. CDB_REPORT_BITS,
  1907. &portsReported) == ERROR_SUCCESS)) {
  1908. if (!(portUsage[0] & 0x1)) {
  1909. comPortNumber = 1;
  1910. }
  1911. else if (!(portUsage[0] & 0x2)) {
  1912. comPortNumber = 2;
  1913. }
  1914. else if (!(portUsage[0] & 0x4)) {
  1915. comPortNumber = 3;
  1916. }
  1917. else if (!(portUsage[0] & 0x8)) {
  1918. comPortNumber = 4;
  1919. }
  1920. else {
  1921. comPortNumber = NO_COM_NUMBER;
  1922. }
  1923. }
  1924. }
  1925. if (comPortNumber == NO_COM_NUMBER) {
  1926. if (hComDB == HCOMDB_INVALID_HANDLE_VALUE) {
  1927. //
  1928. // Couldn't open the DB, pick a com port number that doesn't conflict
  1929. // with any firmware ports
  1930. //
  1931. comPortNumber = DEF_MIN_COM_NUM;
  1932. }
  1933. else {
  1934. //
  1935. // Let the db find the next number
  1936. //
  1937. ComDBClaimNextFreePort(hComDB,
  1938. &comPortNumber);
  1939. }
  1940. }
  1941. else {
  1942. //
  1943. // We have been told what number to use, claim it irregardless of what
  1944. // has already been claimed
  1945. //
  1946. ComDBClaimPort(hComDB,
  1947. comPortNumber,
  1948. TRUE,
  1949. NULL);
  1950. }
  1951. if (hComDB != HCOMDB_INVALID_HANDLE_VALUE) {
  1952. ComDBClose(hComDB);
  1953. }
  1954. //
  1955. // Generate the serial and COM port names based on the numbers we picked.
  1956. //
  1957. wsprintf(szPortName, TEXT("%s%d"), m_szCOM, comPortNumber);
  1958. //
  1959. // Write out Device Parameters\PortName and PollingPeriod
  1960. //
  1961. if((hKey = SetupDiCreateDevRegKey(DeviceInfoSet,
  1962. DeviceInfoData,
  1963. DICS_FLAG_GLOBAL,
  1964. 0,
  1965. DIREG_DEV,
  1966. NULL,
  1967. NULL)) != INVALID_HANDLE_VALUE) {
  1968. DWORD PollingPeriod = PollingPeriods[POLL_PERIOD_DEFAULT_IDX];
  1969. //
  1970. // A failure is not catastrophic, serial will just not know what to call
  1971. // the port
  1972. //
  1973. RegSetValueEx(hKey,
  1974. m_szPortName,
  1975. 0,
  1976. REG_SZ,
  1977. (PBYTE) szPortName,
  1978. ByteCountOf(lstrlen(szPortName) + 1)
  1979. );
  1980. RegSetValueEx(hKey,
  1981. m_szPollingPeriod,
  1982. 0,
  1983. REG_DWORD,
  1984. (PBYTE) &PollingPeriod,
  1985. sizeof(DWORD)
  1986. );
  1987. RegCloseKey(hKey);
  1988. }
  1989. //
  1990. // Now do the installation for this device.
  1991. //
  1992. if(!SetupDiInstallDevice(DeviceInfoSet, DeviceInfoData)) {
  1993. return GetLastError();
  1994. }
  1995. //
  1996. // Write out the friendly name based on the device desc
  1997. //
  1998. if (LoadString(g_hInst,
  1999. IDS_FRIENDLY_FORMAT,
  2000. friendlyNameFormat,
  2001. CharSizeOf(friendlyNameFormat)) &&
  2002. SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
  2003. DeviceInfoData,
  2004. SPDRP_DEVICEDESC,
  2005. NULL,
  2006. (PBYTE)deviceDesc,
  2007. sizeof(deviceDesc),
  2008. NULL)) {
  2009. wsprintf(charBuffer, friendlyNameFormat, deviceDesc, szPortName);
  2010. }
  2011. else {
  2012. lstrcpy(charBuffer, szPortName);
  2013. }
  2014. // Write the string friendly name string out
  2015. SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
  2016. DeviceInfoData,
  2017. SPDRP_FRIENDLYNAME,
  2018. (PBYTE)charBuffer,
  2019. ByteCountOf(lstrlen(charBuffer) + 1)
  2020. );
  2021. //
  2022. // Write out the default settings to win.ini (really a registry key) if they
  2023. // don't already exist.
  2024. //
  2025. wcscat(szPortName, m_szColon);
  2026. charBuffer[0] = TEXT('\0');
  2027. GetProfileString(m_szPorts,
  2028. szPortName,
  2029. TEXT(""),
  2030. charBuffer,
  2031. sizeof(charBuffer) / sizeof(TCHAR) );
  2032. //
  2033. // Check to see if the default string provided was copied in, if so, write
  2034. // out the port defaults
  2035. //
  2036. if (charBuffer[0] == TEXT('\0')) {
  2037. WriteProfileString(m_szPorts, szPortName, m_szDefParams);
  2038. }
  2039. return NO_ERROR;
  2040. }
  2041. // @@BEGIN_DDKSPLIT
  2042. BOOL
  2043. GetSerialPortDevInstConfig(
  2044. IN DEVINST DevInst,
  2045. IN ULONG LogConfigType,
  2046. OUT PIO_RESOURCE IoResource, OPTIONAL
  2047. OUT PIRQ_RESOURCE IrqResource OPTIONAL
  2048. )
  2049. /*++
  2050. Routine Description:
  2051. This routine retrieves the base IO port and IRQ for the specified device instance
  2052. in a particular logconfig.
  2053. Arguments:
  2054. DevInst - Supplies the handle of a device instance to retrieve configuration for.
  2055. LogConfigType - Specifies the type of logconfig to retrieve. Must be either
  2056. ALLOC_LOG_CONF, BOOT_LOG_CONF, or FORCED_LOG_CONF.
  2057. IoResource - Optionally, supplies the address of an Io resource structure
  2058. that receives the Io resource retreived.
  2059. IrqResource - Optionally, supplies the address of an IRQ resource variable
  2060. that receives the IRQ resource retrieved.
  2061. AdditionalResources - Optionally, supplies the address of a CM_RESOURCE_LIST pointer.
  2062. If this parameter is specified, then this pointer will be filled in with the
  2063. address of a newly-allocated buffer containing any additional resources contained
  2064. in this logconfig. If there are no additional resources (which will typically be
  2065. the case), then this pointer will be set to NULL.
  2066. The caller is responsible for freeing this buffer.
  2067. AdditionalResourcesSize - Optionally, supplies the address of a variable that receives
  2068. the size, in bytes, of the buffer allocated and returned in the AdditionalResources
  2069. parameter. If that parameter is not specified, then this parameter is ignored.
  2070. Return Value:
  2071. If success, the return value is TRUE, otherwise it is FALSE.
  2072. --*/
  2073. {
  2074. LOG_CONF LogConfig;
  2075. RES_DES ResDes;
  2076. CONFIGRET cr;
  2077. BOOL Success;
  2078. PBYTE ResDesBuffer = NULL;
  2079. ULONG ResDesBufferSize = 68; // big enough for everything but class-specific resource.
  2080. if(CM_Get_First_Log_Conf(&LogConfig, DevInst, LogConfigType) != CR_SUCCESS) {
  2081. return FALSE;
  2082. }
  2083. Success = FALSE; // assume failure.
  2084. //
  2085. // First, get the Io base port
  2086. //
  2087. if(IoResource) {
  2088. if(CM_Get_Next_Res_Des(&ResDes, LogConfig, ResType_IO, NULL, 0) != CR_SUCCESS) {
  2089. goto clean0;
  2090. }
  2091. cr = CM_Get_Res_Des_Data(ResDes, IoResource, sizeof(IO_RESOURCE), 0);
  2092. CM_Free_Res_Des_Handle(ResDes);
  2093. if(cr != CR_SUCCESS) {
  2094. goto clean0;
  2095. }
  2096. }
  2097. //
  2098. // Now, get the IRQ
  2099. //
  2100. if(IrqResource) {
  2101. if(CM_Get_Next_Res_Des(&ResDes, LogConfig, ResType_IRQ, NULL, 0) != CR_SUCCESS) {
  2102. goto clean0;
  2103. }
  2104. cr = CM_Get_Res_Des_Data(ResDes, IrqResource, sizeof(IRQ_RESOURCE), 0);
  2105. CM_Free_Res_Des_Handle(ResDes);
  2106. if(cr != CR_SUCCESS) {
  2107. goto clean0;
  2108. }
  2109. }
  2110. Success = TRUE;
  2111. clean0:
  2112. CM_Free_Log_Conf_Handle(LogConfig);
  2113. if(ResDesBuffer) {
  2114. LocalFree(ResDesBuffer);
  2115. }
  2116. return Success;
  2117. }
  2118. // @@END_DDKSPLIT
  2119. void InitStrings(void)
  2120. {
  2121. DWORD dwClass, dwShare;
  2122. TCHAR szClass[ 40 ];
  2123. LoadString(g_hInst,
  2124. INITS,
  2125. g_szErrMem,
  2126. CharSizeOf(g_szErrMem));
  2127. LoadString(g_hInst,
  2128. IDS_INIT_NAME,
  2129. g_szPortsApplet,
  2130. CharSizeOf(g_szPortsApplet));
  2131. //
  2132. // Get the "Close" string
  2133. //
  2134. LoadString(g_hInst,
  2135. IDS_INIT_CLOSE,
  2136. g_szClose,
  2137. CharSizeOf(g_szClose));
  2138. }