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

1744 lines
45 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. ENUM.C
  5. Abstract:
  6. This module contains the enumeration code needed to figure out
  7. whether or not a device is attached to the serial port. If there
  8. is one, it will obtain the PNP COM ID (if the device is PNP) and
  9. parse out the relevant fields.
  10. @@BEGIN_DDKSPLIT
  11. Author:
  12. Jay Senior
  13. @@END_DDKSPLIT
  14. Environment:
  15. kernel mode only
  16. Notes:
  17. @@BEGIN_DDKSPLIT
  18. Revision History:
  19. Louis J. Giliberto, Jr. 22-March-1998 Cleanup
  20. Louis J. Giliberto, Jr. 11-Jan-2000 Cleanup / fix postponed
  21. @@END_DDKSPLIT
  22. --*/
  23. #include "pch.h"
  24. #define MAX_DEVNODE_NAME 256 // Total size of Device ID
  25. #ifdef ALLOC_PRAGMA
  26. #pragma alloc_text(PAGESENM, SerenumValidateID)
  27. #pragma alloc_text(PAGESENM, SerenumDoEnumProtocol)
  28. #pragma alloc_text(PAGESENM, SerenumCheckForLegacyDevice)
  29. #pragma alloc_text(PAGESENM, SerenumScanOtherIdForMouse)
  30. #pragma alloc_text(PAGESENM, Serenum_ReenumerateDevices)
  31. #pragma alloc_text(PAGESENM, Serenum_IoSyncReq)
  32. #pragma alloc_text(PAGESENM, Serenum_IoSyncReqWithIrp)
  33. #pragma alloc_text(PAGESENM, Serenum_IoSyncIoctlEx)
  34. #pragma alloc_text(PAGESENM, Serenum_ReadSerialPort)
  35. #pragma alloc_text(PAGESENM, Serenum_Wait)
  36. #pragma alloc_text(PAGESENM, SerenumReleaseThreadReference)
  37. //#pragma alloc_text (PAGE, Serenum_GetRegistryKeyValue)
  38. #endif
  39. #if !defined(__isascii)
  40. #define __isascii(_c) ( (unsigned)(_c) < 0x80 )
  41. #endif // !defined(__isascii)
  42. void
  43. SerenumScanOtherIdForMouse(IN PCHAR PBuffer, IN ULONG BufLen,
  44. OUT PCHAR *PpMouseId)
  45. /*++
  46. Routine Description:
  47. This routines a PnP packet for a mouse ID up to the first PnP delimiter
  48. (i.e, '(').
  49. Arguments:
  50. PBuffer - Pointer to the buffer to scan
  51. BufLen - Length of the buffer in bytes
  52. PpMouseId - Pointer to the pointer to the mouse ID (this will be set
  53. to point to the location in the buffer where the mouse ID
  54. was found)
  55. Return value:
  56. void
  57. --*/
  58. {
  59. PAGED_CODE();
  60. *PpMouseId = PBuffer;
  61. while (BufLen--) {
  62. if (**PpMouseId == 'M' || **PpMouseId == 'B') {
  63. return;
  64. } else if (**PpMouseId == '(' || **PpMouseId == ('(' - 0x20)) {
  65. *PpMouseId = NULL;
  66. return;
  67. }
  68. (*PpMouseId)++;
  69. }
  70. *PpMouseId = NULL;
  71. }
  72. #if DBG
  73. VOID SerenumHexDump(PUCHAR PBuf, ULONG NBytes)
  74. /*++
  75. Routine Description:
  76. Hex dump a buffer with NPerRow chars per row output
  77. Arguments:
  78. PBuf - Pointer to the buffer to dump
  79. NBytes - Length of the buffer in bytes
  80. Return value:
  81. VOID
  82. --*/
  83. {
  84. const ULONG NPerRow = 20;
  85. ULONG dmpi;
  86. ULONG col;
  87. UCHAR c;
  88. ULONG LoopCount = 1;
  89. ULONG dividend = NBytes / NPerRow;
  90. ULONG remainder = NBytes % NPerRow;
  91. ULONG nHexChars = NPerRow;
  92. ULONG nSpaces = 1;
  93. DbgPrint("SERENUM: Raw Data Packet on probe\n");
  94. if (remainder) {
  95. LoopCount++;
  96. }
  97. for (dmpi = 0; dmpi < (dividend + 1); dmpi++) {
  98. DbgPrint("-------: ");
  99. for (col = 0; col < nHexChars; col++) {
  100. DbgPrint("%02x ", (unsigned char)PBuf[dmpi * NPerRow + col]);
  101. }
  102. for (col = 0; col < nSpaces; col++) {
  103. DbgPrint(" ");
  104. }
  105. for (col = 0; col < nHexChars; col++){
  106. c = PBuf[dmpi * NPerRow + col];
  107. if (__isascii(c) && (c > ' ')){
  108. DbgPrint("%c", c);
  109. }else{
  110. DbgPrint(".");
  111. }
  112. }
  113. DbgPrint("\n");
  114. //
  115. // If this is the last one, then we have less that NPerRow to dump
  116. //
  117. if (dmpi == dividend) {
  118. if (remainder == 0) {
  119. //
  120. // This was an even multiple -- we're done
  121. //
  122. break; // for (dmpi)
  123. } else {
  124. nHexChars = remainder;
  125. nSpaces = NPerRow - nHexChars;
  126. }
  127. }
  128. }
  129. }
  130. #endif // DBG
  131. NTSTATUS
  132. SerenumDoEnumProtocol(PFDO_DEVICE_DATA PFdoData, PUCHAR *PpBuf, PUSHORT PNBytes,
  133. PBOOLEAN PDSRMissing)
  134. {
  135. IO_STATUS_BLOCK ioStatusBlock;
  136. ULONG i;
  137. ULONG bitMask;
  138. KEVENT event;
  139. KTIMER timer;
  140. NTSTATUS status;
  141. PUCHAR pReadBuf;
  142. USHORT nRead;
  143. PDEVICE_OBJECT pDevStack = PFdoData->TopOfStack;
  144. SERIAL_BAUD_RATE baudRate;
  145. SERIAL_LINE_CONTROL lineControl;
  146. SERIAL_HANDFLOW handflow;
  147. #if DBG
  148. #define PERFCNT 1
  149. #endif
  150. #if defined(PERFCNT)
  151. LARGE_INTEGER perfFreq;
  152. LARGE_INTEGER stPerfCnt, endPerfCnt;
  153. LONG diff;
  154. #endif
  155. LARGE_INTEGER DefaultWait;
  156. PAGED_CODE();
  157. KeInitializeEvent(&event, NotificationEvent, FALSE);
  158. KeInitializeTimer(&timer);
  159. #if defined(PERFCNT)
  160. perfFreq.QuadPart = (LONGLONG) 0;
  161. #endif
  162. DefaultWait.QuadPart = (LONGLONG) -(SERENUM_DEFAULT_WAIT);
  163. *PpBuf = NULL;
  164. pReadBuf = NULL;
  165. nRead = 0;
  166. *PDSRMissing = FALSE;
  167. LOGENTRY(LOG_ENUM, 'SDEP', PFdoData, PpBuf, PDSRMissing);
  168. pReadBuf = ExAllocatePool(NonPagedPool, MAX_DEVNODE_NAME);
  169. if (pReadBuf == NULL) {
  170. status = STATUS_INSUFFICIENT_RESOURCES;
  171. LOGENTRY(LOG_ENUM, 'SDE1', PFdoData, status, 0);
  172. goto ProtocolDone;
  173. }
  174. //
  175. // Set DTR
  176. //
  177. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting DTR...\n"));
  178. status = Serenum_IoSyncIoctl(IOCTL_SERIAL_SET_DTR, FALSE, pDevStack, &event);
  179. if (!NT_SUCCESS(status)) {
  180. LOGENTRY(LOG_ENUM, 'SDE2', PFdoData, status, 0);
  181. goto ProtocolDone;
  182. }
  183. //
  184. // Clear RTS
  185. //
  186. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Clearing RTS...\n"));
  187. status = Serenum_IoSyncIoctl(IOCTL_SERIAL_CLR_RTS, FALSE, pDevStack, &event);
  188. if (!NT_SUCCESS(status)) {
  189. LOGENTRY(LOG_ENUM, 'SDE3', PFdoData, status, 0);
  190. goto ProtocolDone;
  191. }
  192. //
  193. // Wait for the default timeout period
  194. //
  195. #if defined(PERFCNT)
  196. stPerfCnt = KeQueryPerformanceCounter(&perfFreq);
  197. #endif
  198. status = Serenum_Wait(&timer, DefaultWait);
  199. #if defined(PERFCNT)
  200. endPerfCnt = KeQueryPerformanceCounter(NULL);
  201. diff = (LONG)(endPerfCnt.QuadPart - stPerfCnt.QuadPart);
  202. diff *= 1000;
  203. diff /= (LONG)perfFreq.QuadPart;
  204. LOGENTRY(LOG_ENUM, 'SDT0', PFdoData, diff, 0);
  205. #endif
  206. if (!NT_SUCCESS(status)) {
  207. Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
  208. ("Timer failed with status %x\n", status ));
  209. LOGENTRY(LOG_ENUM, 'SDE4', PFdoData, status, 0);
  210. goto ProtocolDone;
  211. }
  212. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Checking DSR...\n"));
  213. status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_GET_MODEMSTATUS, FALSE,
  214. pDevStack, &event, NULL, 0, &bitMask,
  215. sizeof(ULONG));
  216. if (!NT_SUCCESS(status)) {
  217. LOGENTRY(LOG_ENUM, 'SDE5', PFdoData, status, 0);
  218. goto ProtocolDone;
  219. }
  220. //
  221. // If DSR is not set, then a legacy device (like a mouse) may be attached --
  222. // they are not required to assert DSR when they are present and ready.
  223. //
  224. if ((SERIAL_DSR_STATE & bitMask) == 0) {
  225. Serenum_KdPrint (PFdoData, SER_DBG_SS_TRACE,
  226. ("No PNP device available - DSR not set.\n"));
  227. *PDSRMissing = TRUE;
  228. LOGENTRY(LOG_ENUM, 'SDND', PFdoData, 0, 0);
  229. }
  230. //
  231. // Setup the serial port for 1200 bits/s, 7 data bits,
  232. // no parity, one stop bit
  233. //
  234. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting baud rate to 1200..."
  235. "\n"));
  236. baudRate.BaudRate = 1200;
  237. status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_BAUD_RATE, FALSE, pDevStack,
  238. &event, &baudRate, sizeof(SERIAL_BAUD_RATE),
  239. NULL, 0);
  240. if (!NT_SUCCESS(status)) {
  241. LOGENTRY(LOG_ENUM, 'SDE6', PFdoData, status, 0);
  242. goto ProtocolDone;
  243. }
  244. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  245. ("Setting the line control...\n"));
  246. lineControl.StopBits = STOP_BIT_1;
  247. lineControl.Parity = NO_PARITY;
  248. lineControl.WordLength = 7;
  249. status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_LINE_CONTROL, FALSE,
  250. pDevStack, &event, &lineControl,
  251. sizeof(SERIAL_LINE_CONTROL), NULL, 0);
  252. if (!NT_SUCCESS(status)) {
  253. LOGENTRY(LOG_ENUM, 'SDE7', PFdoData, status, 0);
  254. goto ProtocolDone;
  255. }
  256. //
  257. // loop twice
  258. // The first iteration is for reading the PNP ID string from modems
  259. // and mice.
  260. // The second iteration is for other devices.
  261. //
  262. for (i = 0; i < 2; i++) {
  263. //
  264. // Purge the buffers before reading
  265. //
  266. LOGENTRY(LOG_ENUM, 'SDEI', PFdoData, i, 0);
  267. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Purging all buffers...\n"));
  268. bitMask = SERIAL_PURGE_RXCLEAR;
  269. status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_PURGE, FALSE, pDevStack,
  270. &event, &bitMask, sizeof(ULONG), NULL, 0);
  271. if (!NT_SUCCESS(status)) {
  272. LOGENTRY(LOG_ENUM, 'SDE8', PFdoData, status, 0);
  273. break;
  274. }
  275. //
  276. // Clear DTR
  277. //
  278. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Clearing DTR...\n"));
  279. status = Serenum_IoSyncIoctl(IOCTL_SERIAL_CLR_DTR, FALSE, pDevStack,
  280. &event);
  281. if (!NT_SUCCESS(status)) {
  282. LOGENTRY(LOG_ENUM, 'SDE9', PFdoData, status, 0);
  283. break;
  284. }
  285. //
  286. // Clear RTS
  287. //
  288. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Clearing RTS...\n"));
  289. status = Serenum_IoSyncIoctl(IOCTL_SERIAL_CLR_RTS, FALSE, pDevStack,
  290. &event);
  291. if (!NT_SUCCESS(status)) {
  292. LOGENTRY(LOG_ENUM, 'SDEA', PFdoData, status, 0);
  293. break;
  294. }
  295. //
  296. // Set a timer for 200 ms
  297. //
  298. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Waiting...\n"));
  299. #if defined(PERFCNT)
  300. stPerfCnt = KeQueryPerformanceCounter(&perfFreq);
  301. #endif
  302. status = Serenum_Wait(&timer, DefaultWait);
  303. if (!NT_SUCCESS(status)) {
  304. Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
  305. ("Timer failed with status %x\n", status ));
  306. LOGENTRY(LOG_ENUM, 'SDEB', PFdoData, status, 0);
  307. break;
  308. }
  309. //
  310. // set DTR
  311. //
  312. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting DTR...\n"));
  313. status = Serenum_IoSyncIoctl(IOCTL_SERIAL_SET_DTR, FALSE, pDevStack,
  314. &event);
  315. if (!NT_SUCCESS(status)) {
  316. LOGENTRY(LOG_ENUM, 'SDEC', PFdoData, status, 0);
  317. break;
  318. }
  319. #if defined(PERFCNT)
  320. endPerfCnt = KeQueryPerformanceCounter(NULL);
  321. diff = (LONG)(endPerfCnt.QuadPart - stPerfCnt.QuadPart);
  322. diff *= 1000;
  323. diff /= (LONG)perfFreq.QuadPart;
  324. LOGENTRY(LOG_ENUM, 'SDT1', PFdoData, diff, 0);
  325. #endif
  326. //
  327. // First iteration is for modems
  328. // Therefore wait for 200 ms as per protocol for getting PNP string out
  329. //
  330. if (!i) {
  331. status = Serenum_Wait(&timer, DefaultWait);
  332. if (!NT_SUCCESS(status)) {
  333. Serenum_KdPrint (PFdoData, SER_DBG_SS_ERROR,
  334. ("Timer failed with status %x\n", status ));
  335. LOGENTRY(LOG_ENUM, 'SDED', PFdoData, status, 0);
  336. break;
  337. }
  338. }
  339. //
  340. // set RTS
  341. //
  342. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting RTS...\n"));
  343. status = Serenum_IoSyncIoctl(IOCTL_SERIAL_SET_RTS, FALSE, pDevStack,
  344. &event);
  345. if (!NT_SUCCESS(status)) {
  346. LOGENTRY(LOG_ENUM, 'SDEF', PFdoData, status, 0);
  347. break;
  348. }
  349. //
  350. // Read from the serial port
  351. //
  352. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  353. ("Reading the serial port...\n"));
  354. Serenum_KdPrint(PFdoData, SER_DBG_SS_INFO, ("Address: %x\n", pReadBuf));
  355. nRead = 0;
  356. #if DBG
  357. RtlFillMemory(pReadBuf, MAX_DEVNODE_NAME, 0xff);
  358. #endif
  359. //
  360. // Flush the input buffer
  361. //
  362. status = Serenum_ReadSerialPort(pReadBuf, MAX_DEVNODE_NAME,
  363. SERENUM_SERIAL_READ_TIME, &nRead,
  364. &ioStatusBlock, PFdoData);
  365. switch (status) {
  366. case STATUS_TIMEOUT:
  367. if (nRead == 0) {
  368. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  369. ("Timeout with no bytes read; continuing\n"));
  370. LOGENTRY(LOG_ENUM, 'SDEG', PFdoData, status, 0);
  371. continue;
  372. }
  373. //
  374. // We timed out with data, so we use what we have
  375. //
  376. status = STATUS_SUCCESS;
  377. LOGENTRY(LOG_ENUM, 'SDEH', PFdoData, status, 0);
  378. break;
  379. case STATUS_SUCCESS:
  380. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Read succeeded\n"));
  381. LOGENTRY(LOG_ENUM, 'SDEJ', PFdoData, status, 0);
  382. goto ProtocolDone;
  383. break;
  384. default:
  385. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Read failed with 0x%x\n",
  386. status));
  387. LOGENTRY(LOG_ENUM, 'SDEK', PFdoData, status, 0);
  388. goto ProtocolDone;
  389. break;
  390. }
  391. //
  392. // If anything was read from the serial port, we're done!
  393. //
  394. if (nRead) {
  395. break;
  396. }
  397. }
  398. ProtocolDone:;
  399. if (!NT_SUCCESS(status)) {
  400. if (pReadBuf != NULL) {
  401. ExFreePool(pReadBuf);
  402. pReadBuf = NULL;
  403. }
  404. }
  405. *PNBytes = nRead;
  406. *PpBuf = pReadBuf;
  407. LOGENTRY(LOG_ENUM, 'SDE0', PFdoData, status, nRead);
  408. return status;
  409. }
  410. BOOLEAN
  411. SerenumValidateID(IN PUNICODE_STRING PId)
  412. /*++
  413. Routine Description:
  414. This validates all the characters in a MULTI_SZ for a Pnp ID.
  415. Invalid characters are:
  416. c < 0x20 (' ')
  417. c > 0x7F
  418. c == 0x2C (',')
  419. Arguments:
  420. PId - Pointer to a multi_sz containing the IDs
  421. Return value:
  422. BOOLEAN -- TRUE if valid ID, FALSE otherwise
  423. --*/
  424. {
  425. WCHAR *cp;
  426. PAGED_CODE();
  427. //
  428. // Walk each string in the multisz and check for bad characters
  429. //
  430. cp = PId->Buffer;
  431. if (cp == NULL) {
  432. return TRUE;
  433. }
  434. do {
  435. while (*cp) {
  436. if ((*cp < L' ') || (*cp > L'\x7f') || (*cp == L',') ) {
  437. return FALSE;
  438. }
  439. cp++;
  440. }
  441. cp++;
  442. } while (*cp);
  443. return TRUE;
  444. }
  445. BOOLEAN
  446. SerenumCheckForLegacyDevice(IN PFDO_DEVICE_DATA PFdoData, IN PCHAR PIdBuf,
  447. IN ULONG BufferLen,
  448. IN OUT PUNICODE_STRING PHardwareIDs,
  449. IN OUT PUNICODE_STRING PCompIDs,
  450. IN OUT PUNICODE_STRING PDeviceIDs)
  451. /*++
  452. Routine Description:
  453. This routine implements legacy mouse detection
  454. Arguments:
  455. PFdoData - pointer to the FDO's device-specific data
  456. PIdBuf - Buffer of data returned from device
  457. BufferLen - length of PIdBuf in bytes
  458. PHardwareIDs - MULTI_SZ to return hardware ID's in
  459. PCompIDs - MULTI_SZ to return compatible ID's in
  460. PDeviceIDs - MULTI_SZ to return device ID's in
  461. Return value:
  462. BOOLEAN -- TRUE if mouse detected, FALSE otherwise
  463. --*/
  464. {
  465. PCHAR mouseId = PIdBuf;
  466. ULONG charCnt;
  467. BOOLEAN rval = FALSE;
  468. PAGED_CODE();
  469. SerenumScanOtherIdForMouse(PIdBuf, BufferLen, &mouseId);
  470. if (mouseId != NULL) {
  471. //
  472. // A legacy device is attached to the serial port, since DSR was
  473. // not set when RTS was set.
  474. // If we find a mouse from the PIdBuf, copy the appropriate
  475. // strings into the hardwareIDs and compIDs manually.
  476. //
  477. if (*mouseId == 'M') {
  478. if ((mouseId - PIdBuf) > 1 && mouseId[1] == '3') {
  479. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("*PNP0F08 mouse\n"));
  480. Serenum_InitMultiString(PFdoData, PHardwareIDs, "*PNP0F08", NULL);
  481. Serenum_InitMultiString(PFdoData, PCompIDs, "SERIAL_MOUSE", NULL);
  482. //
  483. // ADRIAO CIMEXCIMEX 04/28/1999 -
  484. // Device ID's should be unique, at least as unique as the
  485. // hardware ID's. This ID should really be Serenum\\PNP0F08
  486. //
  487. Serenum_InitMultiString(PFdoData, PDeviceIDs, "Serenum\\Mouse",
  488. NULL);
  489. rval = TRUE;
  490. } else {
  491. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("*PNP0F01 mouse\n"));
  492. Serenum_InitMultiString(PFdoData, PHardwareIDs, "*PNP0F01", NULL);
  493. Serenum_InitMultiString(PFdoData, PCompIDs, "SERIAL_MOUSE", NULL);
  494. //
  495. // ADRIAO CIMEXCIMEX 04/28/1999 -
  496. // Device ID's should be unique, at least as unique as the
  497. // hardware ID's. This ID should really be Serenum\\PNP0F01
  498. //
  499. Serenum_InitMultiString(PFdoData, PDeviceIDs, "Serenum\\Mouse",
  500. NULL);
  501. rval = TRUE;
  502. }
  503. } else if (*mouseId == 'B') {
  504. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("*PNP0F09 mouse\n"));
  505. Serenum_InitMultiString(PFdoData, PHardwareIDs, "*PNP0F09", NULL);
  506. Serenum_InitMultiString(PFdoData, PCompIDs, "*PNP0F0F", "SERIAL_MOUSE",
  507. NULL);
  508. //
  509. // ADRIAO CIMEXCIMEX 04/28/1999 -
  510. // Device ID's should be unique, at least as unique as the
  511. // hardware ID's. This ID should really be Serenum\\PNP0F09
  512. //
  513. Serenum_InitMultiString(PFdoData, PDeviceIDs, "Serenum\\BallPoint",
  514. NULL);
  515. rval = TRUE;
  516. }
  517. #if DBG
  518. if (rval) {
  519. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  520. ("Buffers at 0x%x 0x%x 0x%x\n",
  521. PHardwareIDs->Buffer, PCompIDs->Buffer,
  522. PDeviceIDs->Buffer));
  523. }
  524. #endif // DBG
  525. }
  526. return rval;
  527. }
  528. NTSTATUS
  529. Serenum_ReenumerateDevices(IN PIRP Irp, IN PFDO_DEVICE_DATA PFdoData,
  530. PBOOLEAN PSameDevice)
  531. /*++
  532. Routine Description:
  533. This enumerates the serenum bus which is represented by Fdo (a pointer
  534. to the device object representing the serial bus). It creates new PDOs
  535. for any new devices which have been discovered since the last enumeration
  536. Arguments:
  537. PFdoData - Pointer to the fdo's device extension
  538. for the serial bus which needs to be enumerated
  539. Irp - Pointer to the Irp which was sent to reenumerate.
  540. Return value:
  541. NTSTATUS
  542. --*/
  543. {
  544. NTSTATUS status;
  545. KEVENT event;
  546. KTIMER timer;
  547. UNICODE_STRING pdoUniName;
  548. PDEVICE_OBJECT pdo = PFdoData->NewPDO;
  549. PDEVICE_OBJECT pDevStack = PFdoData->TopOfStack;
  550. PPDO_DEVICE_DATA pdoData;
  551. UNICODE_STRING hardwareIDs;
  552. UNICODE_STRING compIDs;
  553. UNICODE_STRING deviceIDs;
  554. UNICODE_STRING devDesc;
  555. UNICODE_STRING serNo;
  556. UNICODE_STRING pnpRev;
  557. HANDLE pnpKey;
  558. BOOLEAN DSRMissing = FALSE;
  559. BOOLEAN legacyDeviceFound = FALSE;
  560. USHORT nActual = 0;
  561. ULONG i;
  562. PCHAR pReadBuf = NULL;
  563. WCHAR pdoName[] = SERENUM_PDO_NAME_BASE;
  564. SERIAL_BASIC_SETTINGS basicSettings;
  565. BOOLEAN basicSettingsDone = FALSE;
  566. SERIAL_TIMEOUTS timeouts, newTimeouts;
  567. BOOLEAN validIDs = TRUE;
  568. KIRQL oldIrql;
  569. ULONG curTry = 0;
  570. BOOLEAN sameDevice = FALSE;
  571. PAGED_CODE();
  572. //
  573. // While enumeration is taking place, we can't allow a Create to come down
  574. // from an upper driver. We use this semaphore to protect ourselves.
  575. //
  576. status = KeWaitForSingleObject(&PFdoData->CreateSemaphore, Executive,
  577. KernelMode, FALSE, NULL);
  578. if (!NT_SUCCESS(status)) {
  579. return status;
  580. }
  581. //
  582. // Initialization
  583. //
  584. RtlInitUnicodeString(&pdoUniName, pdoName);
  585. pdoName[((sizeof(pdoName)/sizeof(WCHAR)) - 2)] = L'0' + PFdoData->PdoIndex++;
  586. KeInitializeEvent(&event, NotificationEvent, FALSE);
  587. KeInitializeTimer(&timer);
  588. RtlInitUnicodeString(&hardwareIDs, NULL);
  589. RtlInitUnicodeString(&compIDs, NULL);
  590. RtlInitUnicodeString(&deviceIDs, NULL);
  591. RtlInitUnicodeString(&devDesc, NULL);
  592. RtlInitUnicodeString(&serNo, NULL);
  593. RtlInitUnicodeString(&pnpRev, NULL);
  594. //
  595. // If the current PDO should be marked missing, do so.
  596. //
  597. if (PFdoData->PDOForcedRemove) {
  598. Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
  599. pdo = NULL;
  600. }
  601. //
  602. // Open the Serial port before sending Irps down
  603. // Use the Irp passed to us, and grab it on the way up.
  604. //
  605. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  606. ("Opening the serial port...\n"));
  607. status = Serenum_IoSyncReqWithIrp(Irp, IRP_MJ_CREATE, &event, pDevStack);
  608. LOGENTRY(LOG_ENUM, 'SRRO', PFdoData, status, 0);
  609. //
  610. // If we cannot open the stack, odd's are we have a live and started PDO on
  611. // it. Since enumeration might interfere with running devices, we do not
  612. // adjust our list of children if we cannot open the stack.
  613. //
  614. if (!NT_SUCCESS(status)) {
  615. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  616. ("Failed to open the serial port...\n"));
  617. KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
  618. LOGENTRY(LOG_ENUM, 'SRR1', PFdoData, status, 0);
  619. return status;
  620. }
  621. //
  622. // Set up the COM port
  623. //
  624. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Setting up port\n"));
  625. status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS, TRUE,
  626. pDevStack, &event, NULL, 0,
  627. &basicSettings, sizeof(basicSettings));
  628. if (NT_SUCCESS(status)) {
  629. basicSettingsDone = TRUE;
  630. } else {
  631. //
  632. // This "serial" driver doesn't support BASIC_SETTINGS so instead
  633. // we just set what we really need the old fashioned way
  634. //
  635. Serenum_IoSyncIoctlEx(IOCTL_SERIAL_GET_TIMEOUTS, FALSE, pDevStack, &event,
  636. NULL, 0, &timeouts, sizeof(timeouts));
  637. RtlZeroMemory(&newTimeouts, sizeof(newTimeouts));
  638. Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_TIMEOUTS, FALSE, pDevStack, &event,
  639. &newTimeouts, sizeof(newTimeouts), NULL, 0);
  640. }
  641. //
  642. // Run the serial PnP device detection protocol; give it up to 3 tries
  643. //
  644. while (curTry <= 2) {
  645. if (pReadBuf) {
  646. ExFreePool(pReadBuf);
  647. pReadBuf = NULL;
  648. }
  649. status = SerenumDoEnumProtocol(PFdoData, &pReadBuf, &nActual,
  650. &DSRMissing);
  651. if (status == STATUS_SUCCESS) {
  652. break;
  653. }
  654. curTry++;
  655. }
  656. //
  657. // If DSR wasn't set any existing pdos will be eliminated
  658. //
  659. if (basicSettingsDone) {
  660. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  661. ("Restoring basic settings\n"));
  662. Serenum_IoSyncIoctlEx(IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS, TRUE,
  663. pDevStack, &event, &basicSettings,
  664. sizeof(basicSettings), NULL, 0);
  665. } else {
  666. Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_TIMEOUTS, FALSE, pDevStack, &event,
  667. &timeouts, sizeof(timeouts), NULL, 0);
  668. }
  669. //
  670. // Cleanup and then Close
  671. //
  672. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  673. ("Cleanup on the serial port...\n"));
  674. //
  675. // We ignore the status -- we have to finish closing
  676. //
  677. (void)Serenum_IoSyncReqWithIrp(Irp, IRP_MJ_CLEANUP, &event, pDevStack);
  678. #if DBG
  679. if (!NT_SUCCESS(status)) {
  680. Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
  681. ("Failed to cleanup the serial port...\n"));
  682. // don't return because we want to attempt to close!
  683. }
  684. #endif
  685. //
  686. // Close the Serial port after everything is done
  687. //
  688. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  689. ("Closing the serial port...\n"));
  690. //
  691. // We ignore the status -- we have to close!!
  692. //
  693. Serenum_IoSyncReqWithIrp(Irp, IRP_MJ_CLOSE, &event, pDevStack);
  694. LOGENTRY(LOG_ENUM, 'SRRC', PFdoData, 0, 0);
  695. //
  696. // Our status is that of the enumeration
  697. //
  698. if (!NT_SUCCESS(status)) {
  699. Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
  700. ("Failed to enumerate the serial port...\n"));
  701. KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
  702. if (pReadBuf != NULL) {
  703. ExFreePool(pReadBuf);
  704. }
  705. LOGENTRY(LOG_ENUM, 'SRR2', PFdoData, status, 0);
  706. return status;
  707. }
  708. //
  709. // Check if anything was read, and if not, we're done
  710. //
  711. if (nActual == 0) {
  712. if (pReadBuf != NULL) {
  713. ExFreePool(pReadBuf);
  714. pReadBuf = NULL;
  715. }
  716. if (pdo != NULL) {
  717. //
  718. // Something was there. The device must have been unplugged.
  719. // Remove the PDO.
  720. //
  721. Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
  722. pdo = NULL;
  723. }
  724. goto ExitReenumerate;
  725. }
  726. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  727. ("Something was read from the serial port...\n"));
  728. #if 0
  729. if (PFdoData->DebugLevel & SER_DBG_PNP_DUMP_PACKET) {
  730. SerenumHexDump(pReadBuf, nActual);
  731. }
  732. #endif
  733. //
  734. // Determine from the result whether the current pdo (if we have one),
  735. // should be deleted. If it's the same device, then keep it. If it's a
  736. // different device or if the device is a legacy device, then create a
  737. // new pdo.
  738. //
  739. if (DSRMissing) {
  740. legacyDeviceFound
  741. = SerenumCheckForLegacyDevice(PFdoData, pReadBuf, nActual,
  742. &hardwareIDs, &compIDs, &deviceIDs);
  743. }
  744. if (!legacyDeviceFound) {
  745. //
  746. // No legacy device was found, so parse the data we got back
  747. // from the device.
  748. //
  749. status = Serenum_ParseData(PFdoData, pReadBuf, nActual, &hardwareIDs,
  750. &compIDs, &deviceIDs, &devDesc, &serNo, &pnpRev);
  751. //
  752. // Last chance:
  753. //
  754. // 1) DSR is present
  755. // 2) Not a PnP device
  756. //
  757. // There are some devices that are legacy but also assert DSR (e.g., the
  758. // gyropoint mouse). Give it one last shot.
  759. //
  760. if (!DSRMissing && !NT_SUCCESS(status)) {
  761. //
  762. // CIMEXCIMEX Serenum_ParseData() isn't very tidy, so we
  763. // must clean up after them
  764. //
  765. SerenumFreeUnicodeString(&hardwareIDs);
  766. SerenumFreeUnicodeString(&compIDs);
  767. SerenumFreeUnicodeString(&deviceIDs);
  768. SerenumFreeUnicodeString(&devDesc);
  769. SerenumFreeUnicodeString(&serNo);
  770. SerenumFreeUnicodeString(&pnpRev);
  771. if (SerenumCheckForLegacyDevice(PFdoData, pReadBuf, nActual,
  772. &hardwareIDs, &compIDs, &deviceIDs)) {
  773. status = STATUS_SUCCESS;
  774. }
  775. }
  776. //
  777. // If the data can't be parsed and this isn't a legacy device, then
  778. // it is something we don't understand. We bail out at this point
  779. //
  780. if (!NT_SUCCESS(status)) {
  781. Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
  782. ("Failed to parse the data for the new device\n"));
  783. //
  784. // If there is a current PDO, remove it since we can't ID the
  785. // attached device.
  786. //
  787. if (pdo) {
  788. Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
  789. pdo = NULL;
  790. }
  791. SerenumFreeUnicodeString(&hardwareIDs);
  792. SerenumFreeUnicodeString(&compIDs);
  793. SerenumFreeUnicodeString(&deviceIDs);
  794. SerenumFreeUnicodeString(&devDesc);
  795. SerenumFreeUnicodeString(&serNo);
  796. SerenumFreeUnicodeString(&pnpRev);
  797. ExFreePool(pReadBuf);
  798. pReadBuf = NULL;
  799. goto ExitReenumerate;
  800. }
  801. }
  802. //
  803. // We're now finally able to free this read buffer.
  804. //
  805. if (pReadBuf != NULL) {
  806. ExFreePool(pReadBuf);
  807. }
  808. //
  809. // Validate all the ID's -- if any are illegal,
  810. // then we fail the enumeration
  811. //
  812. if (!SerenumValidateID(&hardwareIDs) || !SerenumValidateID(&compIDs)
  813. || !SerenumValidateID(&deviceIDs)) {
  814. //
  815. // If a PDO already exists, mark it missing and get rid
  816. // of it since we don't know what is out there any longer
  817. //
  818. if (pdo) {
  819. Serenum_PDO_EnumMarkMissing(PFdoData, pdo->DeviceExtension);
  820. pdo = NULL;
  821. }
  822. SerenumFreeUnicodeString(&hardwareIDs);
  823. SerenumFreeUnicodeString(&compIDs);
  824. SerenumFreeUnicodeString(&deviceIDs);
  825. SerenumFreeUnicodeString(&devDesc);
  826. SerenumFreeUnicodeString(&serNo);
  827. SerenumFreeUnicodeString(&pnpRev);
  828. goto ExitReenumerate;
  829. }
  830. //
  831. // Check if the current device is the same as the one that we're
  832. // enumerating. If so, we'll just keep the current pdo.
  833. //
  834. if (pdo) {
  835. pdoData = pdo->DeviceExtension;
  836. //
  837. // ADRIAO CIMEXCIMEX 04/28/1999 -
  838. // We should be comparing device ID's here, but the above mentioned
  839. // bug must be fixed first. Note that even this code is broken as it
  840. // doesn't take into account that hardware/compID's are multiSz.
  841. //
  842. if (!(RtlEqualUnicodeString(&pdoData->HardwareIDs, &hardwareIDs, FALSE)
  843. && RtlEqualUnicodeString(&pdoData->CompIDs, &compIDs, FALSE))) {
  844. //
  845. // The ids are not the same, so get rid of this pdo and create a
  846. // new one so that the PNP system will query the ids and find a
  847. // new driver
  848. //
  849. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE, ("Different device."
  850. " Removing PDO %x\n",
  851. pdo));
  852. Serenum_PDO_EnumMarkMissing(PFdoData, pdoData);
  853. pdo = NULL;
  854. } else {
  855. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  856. ("Same device. Keeping current Pdo %x\n", pdo));
  857. sameDevice = TRUE;
  858. }
  859. }
  860. //
  861. // If there isn't a pdo, then create one!
  862. //
  863. if (!pdo) {
  864. //
  865. // Allocate a pdo
  866. //
  867. status = IoCreateDevice(PFdoData->Self->DriverObject,
  868. sizeof(PDO_DEVICE_DATA), &pdoUniName,
  869. FILE_DEVICE_UNKNOWN,
  870. FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &pdo);
  871. if (!NT_SUCCESS(status)) {
  872. Serenum_KdPrint(PFdoData, SER_DBG_SS_ERROR,
  873. ("Create device failed\n"));
  874. KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1,
  875. FALSE);
  876. return status;
  877. }
  878. Serenum_KdPrint(PFdoData, SER_DBG_SS_TRACE,
  879. ("Created PDO on top of filter: %x\n",pdo));
  880. //
  881. // Initialize the rest of the device object
  882. //
  883. pdoData = pdo->DeviceExtension;
  884. //
  885. // Copy our temp buffers over to the DevExt
  886. //
  887. pdoData->HardwareIDs = hardwareIDs;
  888. pdoData->CompIDs = compIDs;
  889. pdoData->DeviceIDs = deviceIDs;
  890. pdoData->DevDesc = devDesc;
  891. pdoData->SerialNo = serNo;
  892. pdoData->PnPRev = pnpRev;
  893. Serenum_InitPDO(pdo, PFdoData);
  894. }
  895. ExitReenumerate:;
  896. KeReleaseSemaphore(&PFdoData->CreateSemaphore, IO_NO_INCREMENT, 1, FALSE);
  897. *PSameDevice = sameDevice;
  898. return STATUS_SUCCESS;
  899. }
  900. void
  901. Serenum_PDO_EnumMarkMissing(PFDO_DEVICE_DATA FdoData, PPDO_DEVICE_DATA PdoData)
  902. /*++
  903. Routine Description:
  904. Removes the attached pdo from the fdo's list of children.
  905. NOTE: THIS FUNCTION CAN ONLY BE CALLED DURING AN ENUMERATION. If called
  906. outside of enumeration, Serenum might delete it's PDO before PnP has
  907. been told the PDO is gone.
  908. Arguments:
  909. FdoData - Pointer to the fdo's device extension
  910. PdoData - Pointer to the pdo's device extension
  911. Return value:
  912. none
  913. --*/
  914. {
  915. KIRQL oldIrql;
  916. Serenum_KdPrint (FdoData, SER_DBG_SS_TRACE, ("Removing Pdo %x\n",
  917. PdoData->Self));
  918. ASSERT(PdoData->Attached);
  919. KeAcquireSpinLock(&FdoData->EnumerationLock, &oldIrql);
  920. PdoData->Attached = FALSE;
  921. FdoData->NewPDO = NULL;
  922. FdoData->NewPdoData = NULL;
  923. FdoData->NewNumPDOs = 0;
  924. FdoData->NewPDOForcedRemove = FALSE;
  925. FdoData->EnumFlags |= SERENUM_ENUMFLAG_DIRTY;
  926. KeReleaseSpinLock(&FdoData->EnumerationLock, oldIrql);
  927. }
  928. NTSTATUS
  929. Serenum_IoSyncReqWithIrp(PIRP PIrp, UCHAR MajorFunction, PKEVENT PEvent,
  930. PDEVICE_OBJECT PDevObj )
  931. /*++
  932. Routine Description:
  933. Performs a synchronous IO request by waiting on the event object
  934. passed to it. The IRP isn't deallocated after this call.
  935. Arguments:
  936. PIrp - The IRP to be used for this request
  937. MajorFunction - The major function
  938. PEvent - An event used to wait for the IRP
  939. PDevObj - The object that we're performing the IO request upon
  940. Return value:
  941. NTSTATUS
  942. --*/
  943. {
  944. PIO_STACK_LOCATION stack;
  945. NTSTATUS status;
  946. stack = IoGetNextIrpStackLocation(PIrp);
  947. stack->MajorFunction = MajorFunction;
  948. KeClearEvent(PEvent);
  949. IoSetCompletionRoutine(PIrp, Serenum_EnumComplete, PEvent, TRUE,
  950. TRUE, TRUE);
  951. status = Serenum_IoSyncReq(PDevObj, PIrp, PEvent);
  952. if (status == STATUS_SUCCESS) {
  953. status = PIrp->IoStatus.Status;
  954. }
  955. return status;
  956. }
  957. NTSTATUS
  958. Serenum_IoSyncIoctlEx(ULONG Ioctl, BOOLEAN Internal, PDEVICE_OBJECT PDevObj,
  959. PKEVENT PEvent, PVOID PInBuffer, ULONG InBufferLen,
  960. PVOID POutBuffer, ULONG OutBufferLen)
  961. /*++
  962. Routine Description:
  963. Performs a synchronous IO control request by waiting on the event object
  964. passed to it. The IRP is deallocated by the IO system when finished.
  965. Return value:
  966. NTSTATUS
  967. --*/
  968. {
  969. PIRP pIrp;
  970. NTSTATUS status;
  971. IO_STATUS_BLOCK IoStatusBlock;
  972. KeClearEvent(PEvent);
  973. // Allocate an IRP - No need to release
  974. // When the next-lower driver completes this IRP, the IO Mgr releases it.
  975. pIrp = IoBuildDeviceIoControlRequest(Ioctl, PDevObj, PInBuffer, InBufferLen,
  976. POutBuffer, OutBufferLen, Internal,
  977. PEvent, &IoStatusBlock);
  978. if (pIrp == NULL) {
  979. Serenum_KdPrint_Def (SER_DBG_SS_ERROR, ("Failed to allocate IRP\n"));
  980. return STATUS_INSUFFICIENT_RESOURCES;
  981. }
  982. status = Serenum_IoSyncReq(PDevObj, pIrp, PEvent);
  983. if (status == STATUS_SUCCESS) {
  984. status = IoStatusBlock.Status;
  985. }
  986. return status;
  987. }
  988. NTSTATUS
  989. Serenum_IoSyncReq(PDEVICE_OBJECT PDevObj, IN PIRP PIrp, PKEVENT PEvent)
  990. /*++
  991. Routine Description:
  992. Performs a synchronous IO request by waiting on the event object
  993. passed to it. The IRP is deallocated by the IO system when finished.
  994. Return value:
  995. NTSTATUS
  996. --*/
  997. {
  998. NTSTATUS status;
  999. status = IoCallDriver(PDevObj, PIrp);
  1000. if (status == STATUS_PENDING) {
  1001. // wait for it...
  1002. status = KeWaitForSingleObject(PEvent, Executive, KernelMode, FALSE,
  1003. NULL);
  1004. }
  1005. return status;
  1006. }
  1007. NTSTATUS
  1008. Serenum_Wait(IN PKTIMER Timer, IN LARGE_INTEGER DueTime)
  1009. /*++
  1010. Routine Description:
  1011. Performs a wait for the specified time.
  1012. NB: Negative time is relative to the current time. Positive time
  1013. represents an absolute time to wait until.
  1014. Return value:
  1015. NTSTATUS
  1016. --*/
  1017. {
  1018. if (KeSetTimer(Timer, DueTime, NULL)) {
  1019. Serenum_KdPrint_Def(SER_DBG_SS_INFO, ("Timer already set: %x\n", Timer));
  1020. }
  1021. return KeWaitForSingleObject(Timer, Executive, KernelMode, FALSE, NULL);
  1022. }
  1023. NTSTATUS
  1024. Serenum_EnumComplete (
  1025. IN PDEVICE_OBJECT DeviceObject,
  1026. IN PIRP Irp,
  1027. IN PVOID Context
  1028. )
  1029. /*++
  1030. Routine Description:
  1031. A completion routine for use when calling the lower device objects to
  1032. which our bus (FDO) is attached. It sets the event for the synchronous
  1033. calls done.
  1034. --*/
  1035. {
  1036. UNREFERENCED_PARAMETER(DeviceObject);
  1037. if (Irp->PendingReturned) {
  1038. IoMarkIrpPending(Irp);
  1039. }
  1040. KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
  1041. // No special priority
  1042. // No Wait
  1043. return STATUS_MORE_PROCESSING_REQUIRED; // Keep this IRP
  1044. }
  1045. NTSTATUS
  1046. Serenum_ReadSerialPort(OUT PCHAR PReadBuffer, IN USHORT Buflen,
  1047. IN ULONG Timeout, OUT PUSHORT nActual,
  1048. OUT PIO_STATUS_BLOCK PIoStatusBlock,
  1049. IN const PFDO_DEVICE_DATA FdoData)
  1050. {
  1051. NTSTATUS status;
  1052. PIRP pIrp;
  1053. LARGE_INTEGER startingOffset;
  1054. KEVENT event;
  1055. SERIAL_TIMEOUTS timeouts;
  1056. ULONG i;
  1057. startingOffset.QuadPart = (LONGLONG) 0;
  1058. //
  1059. // Set the proper timeouts for the read
  1060. //
  1061. timeouts.ReadIntervalTimeout = MAXULONG;
  1062. timeouts.ReadTotalTimeoutMultiplier = MAXULONG;
  1063. timeouts.ReadTotalTimeoutConstant = Timeout;
  1064. timeouts.WriteTotalTimeoutMultiplier = 0;
  1065. timeouts.WriteTotalTimeoutConstant = 0;
  1066. KeInitializeEvent(&event, NotificationEvent, FALSE);
  1067. status = Serenum_IoSyncIoctlEx(IOCTL_SERIAL_SET_TIMEOUTS, FALSE,
  1068. FdoData->TopOfStack, &event, &timeouts,
  1069. sizeof(timeouts), NULL, 0);
  1070. if (!NT_SUCCESS(status)) {
  1071. return status;
  1072. }
  1073. Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Read pending...\n"));
  1074. *nActual = 0;
  1075. while (*nActual < Buflen) {
  1076. KeClearEvent(&event);
  1077. pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, FdoData->TopOfStack,
  1078. PReadBuffer, 1, &startingOffset,
  1079. &event, PIoStatusBlock);
  1080. if (pIrp == NULL) {
  1081. Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Failed to allocate IRP"
  1082. "\n"));
  1083. return STATUS_INSUFFICIENT_RESOURCES;
  1084. }
  1085. status = IoCallDriver(FdoData->TopOfStack, pIrp);
  1086. if (status == STATUS_PENDING) {
  1087. //
  1088. // Wait for the IRP
  1089. //
  1090. status = KeWaitForSingleObject(&event, Executive, KernelMode,
  1091. FALSE, NULL);
  1092. if (status == STATUS_SUCCESS) {
  1093. status = PIoStatusBlock->Status;
  1094. }
  1095. }
  1096. if (!NT_SUCCESS(status) || status == STATUS_TIMEOUT) {
  1097. Serenum_KdPrint (FdoData, SER_DBG_SS_ERROR,
  1098. ("IO Call failed with status %x\n", status));
  1099. return status;
  1100. }
  1101. *nActual += (USHORT)PIoStatusBlock->Information;
  1102. PReadBuffer += (USHORT)PIoStatusBlock->Information;
  1103. }
  1104. return status;
  1105. }
  1106. NTSTATUS
  1107. Serenum_GetRegistryKeyValue(IN HANDLE Handle, IN PWCHAR KeyNameString,
  1108. IN ULONG KeyNameStringLength, IN PVOID Data,
  1109. IN ULONG DataLength, OUT PULONG ActualLength)
  1110. /*++
  1111. Routine Description:
  1112. Reads a registry key value from an already opened registry key.
  1113. Arguments:
  1114. Handle Handle to the opened registry key
  1115. KeyNameString ANSI string to the desired key
  1116. KeyNameStringLength Length of the KeyNameString
  1117. Data Buffer to place the key value in
  1118. DataLength Length of the data buffer
  1119. Return Value:
  1120. STATUS_SUCCESS if all works, otherwise status of system call that
  1121. went wrong.
  1122. --*/
  1123. {
  1124. UNICODE_STRING keyName;
  1125. ULONG length;
  1126. PKEY_VALUE_FULL_INFORMATION fullInfo;
  1127. NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  1128. RtlInitUnicodeString (&keyName, KeyNameString);
  1129. length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength
  1130. + DataLength;
  1131. fullInfo = ExAllocatePool(PagedPool, length);
  1132. if (ActualLength != NULL) {
  1133. *ActualLength = 0;
  1134. }
  1135. if (fullInfo) {
  1136. ntStatus = ZwQueryValueKey(Handle, &keyName, KeyValueFullInformation,
  1137. fullInfo, length, &length);
  1138. if (NT_SUCCESS(ntStatus)) {
  1139. //
  1140. // If there is enough room in the data buffer, copy the output
  1141. //
  1142. if (DataLength >= fullInfo->DataLength) {
  1143. RtlCopyMemory(Data, ((PUCHAR)fullInfo) + fullInfo->DataOffset,
  1144. fullInfo->DataLength);
  1145. if (ActualLength != NULL) {
  1146. *ActualLength = fullInfo->DataLength;
  1147. }
  1148. }
  1149. }
  1150. ExFreePool(fullInfo);
  1151. }
  1152. if (!NT_SUCCESS(ntStatus) && !NT_ERROR(ntStatus)) {
  1153. if (ntStatus == STATUS_BUFFER_OVERFLOW) {
  1154. ntStatus = STATUS_BUFFER_TOO_SMALL;
  1155. } else {
  1156. ntStatus = STATUS_UNSUCCESSFUL;
  1157. }
  1158. }
  1159. return ntStatus;
  1160. }
  1161. VOID
  1162. SerenumWaitForEnumThreadTerminate(IN PFDO_DEVICE_DATA PFdoData)
  1163. {
  1164. KIRQL oldIrql;
  1165. PVOID pThreadObj;
  1166. //
  1167. // Take a reference under the lock so the thread can't disappear on us.
  1168. //
  1169. KeAcquireSpinLock(&PFdoData->EnumerationLock, &oldIrql);
  1170. //
  1171. // If the work item beat us, then the thread is done and we can
  1172. // delete/stop/unload. Otherwise, we have to wait. We can use
  1173. // the reference we stole to hold the object around.
  1174. //
  1175. if (PFdoData->ThreadObj != NULL) {
  1176. pThreadObj = PFdoData->ThreadObj;
  1177. PFdoData->ThreadObj = NULL;
  1178. PFdoData->EnumFlags &= ~SERENUM_ENUMFLAG_PENDING;
  1179. } else {
  1180. pThreadObj = NULL;
  1181. }
  1182. KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
  1183. if (pThreadObj != NULL) {
  1184. KeWaitForSingleObject(pThreadObj, Executive, KernelMode, FALSE, NULL);
  1185. ObDereferenceObject(pThreadObj);
  1186. }
  1187. }
  1188. VOID
  1189. SerenumEnumThreadWorkItem(IN PDEVICE_OBJECT PDevObj, IN PVOID PFdoData)
  1190. {
  1191. PFDO_DEVICE_DATA pFdoData = (PFDO_DEVICE_DATA)PFdoData;
  1192. KIRQL oldIrql;
  1193. PVOID pThreadObj;
  1194. PIO_WORKITEM pWorkItem;
  1195. UNREFERENCED_PARAMETER(PDevObj);
  1196. //
  1197. // See if the delete/stop code beat us to the thread obj.
  1198. // If not, we can derefence the thread.
  1199. //
  1200. KeAcquireSpinLock(&pFdoData->EnumerationLock, &oldIrql);
  1201. if (pFdoData->ThreadObj != NULL) {
  1202. pThreadObj = pFdoData->ThreadObj;
  1203. pFdoData->ThreadObj = NULL;
  1204. pFdoData->EnumFlags &= ~SERENUM_ENUMFLAG_PENDING;
  1205. } else {
  1206. pThreadObj = NULL;
  1207. }
  1208. pWorkItem = pFdoData->EnumWorkItem;
  1209. pFdoData->EnumWorkItem = NULL;
  1210. KeReleaseSpinLock(&pFdoData->EnumerationLock, oldIrql);
  1211. if (pThreadObj != NULL) {
  1212. ObDereferenceObject(pThreadObj);
  1213. }
  1214. IoFreeWorkItem(pWorkItem);
  1215. }
  1216. VOID
  1217. SerenumEnumThread(IN PVOID PFdoData)
  1218. {
  1219. PFDO_DEVICE_DATA pFdoData = (PFDO_DEVICE_DATA)PFdoData;
  1220. PIRP pIrp = NULL;
  1221. PIO_STACK_LOCATION pIrpSp;
  1222. NTSTATUS status;
  1223. KIRQL oldIrql;
  1224. PKTHREAD pThread;
  1225. BOOLEAN sameDevice = TRUE;
  1226. pThread = KeGetCurrentThread();
  1227. KeSetPriorityThread(pThread, HIGH_PRIORITY);
  1228. pIrp = IoAllocateIrp(pFdoData->TopOfStack->StackSize + 1, FALSE);
  1229. if (pIrp == NULL) {
  1230. status = STATUS_INSUFFICIENT_RESOURCES;
  1231. goto SerenumEnumThreadErrOut;
  1232. }
  1233. status = Serenum_ReenumerateDevices(pIrp, pFdoData, &sameDevice);
  1234. SerenumEnumThreadErrOut:
  1235. if (pIrp != NULL) {
  1236. IoFreeIrp(pIrp);
  1237. }
  1238. KeAcquireSpinLock(&pFdoData->EnumerationLock, &oldIrql);
  1239. if ((status == STATUS_SUCCESS) && !sameDevice) {
  1240. pFdoData->EnumFlags |= SERENUM_ENUMFLAG_DIRTY;
  1241. }
  1242. KeReleaseSpinLock(&pFdoData->EnumerationLock, oldIrql);
  1243. if ((status == STATUS_SUCCESS) && !sameDevice) {
  1244. IoInvalidateDeviceRelations(pFdoData->UnderlyingPDO, BusRelations);
  1245. }
  1246. //
  1247. // Queue a work item to release the last reference if remove/stop
  1248. // hasn't already.
  1249. //
  1250. IoQueueWorkItem(pFdoData->EnumWorkItem, SerenumEnumThreadWorkItem,
  1251. DelayedWorkQueue, pFdoData);
  1252. PsTerminateSystemThread(STATUS_SUCCESS);
  1253. }
  1254. NTSTATUS
  1255. SerenumStartProtocolThread(IN PFDO_DEVICE_DATA PFdoData)
  1256. {
  1257. HANDLE hThread;
  1258. NTSTATUS status;
  1259. OBJECT_ATTRIBUTES objAttrib;
  1260. HANDLE handle;
  1261. PVOID tmpObj;
  1262. KIRQL oldIrql;
  1263. PIO_WORKITEM pWorkItem;
  1264. InitializeObjectAttributes(&objAttrib, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
  1265. pWorkItem = IoAllocateWorkItem(PFdoData->Self);
  1266. if (pWorkItem == NULL) {
  1267. return STATUS_INSUFFICIENT_RESOURCES;
  1268. }
  1269. PFdoData->EnumWorkItem = pWorkItem;
  1270. status = PsCreateSystemThread(&handle, THREAD_ALL_ACCESS, NULL, NULL, NULL,
  1271. SerenumEnumThread, PFdoData);
  1272. if (NT_SUCCESS(status)) {
  1273. ASSERT(PFdoData->ThreadObj == NULL);
  1274. //
  1275. // We do this merely to get an object pointer that the remove
  1276. // code can wait on.
  1277. //
  1278. status = ObReferenceObjectByHandle(handle, THREAD_ALL_ACCESS, NULL,
  1279. KernelMode, &tmpObj, NULL);
  1280. KeAcquireSpinLock(&PFdoData->EnumerationLock, &oldIrql);
  1281. if (NT_SUCCESS(status)) {
  1282. PFdoData->ThreadObj = tmpObj;
  1283. KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
  1284. } else {
  1285. //
  1286. // The thread may be done by now, so no one would need to
  1287. // synchronize with it.
  1288. //
  1289. PFdoData->ThreadObj = NULL;
  1290. PFdoData->EnumWorkItem = NULL;
  1291. KeReleaseSpinLock(&PFdoData->EnumerationLock, oldIrql);
  1292. IoFreeWorkItem(pWorkItem);
  1293. }
  1294. //
  1295. // Close the handle so the only references possible are the ones
  1296. // for the thread itself and the one either the work item or
  1297. // remove will take care of
  1298. //
  1299. ZwClose(handle);
  1300. } else {
  1301. PFdoData->EnumWorkItem = NULL;
  1302. IoFreeWorkItem(pWorkItem);
  1303. }
  1304. return status;
  1305. }