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.

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