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.

2186 lines
65 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1993 - 1999
  3. Module Name:
  4. parpnp.c
  5. Abstract:
  6. This file contains the main PnP functions.
  7. see:
  8. - pnpfdo.c for AddDevice and FDO handling of PnP IRPs
  9. - pnppdo.c for PDO handling of PnP IRPs
  10. - pnpnotfy.c for PnP callback entry points
  11. - pnputil.c for PnP utility functions
  12. Author:
  13. Timothy T. Wells
  14. Environment:
  15. Kernel mode
  16. Revision History :
  17. --*/
  18. #include "pch.h"
  19. // used to construct 1284.3 "Dot" name suffixes
  20. // table lookup for integer to WCHAR conversion
  21. WCHAR ParInt2Wchar[] = { L'0', L'1', L'2', L'3' };
  22. //
  23. // Keep track of the number of parallel port devices created...
  24. //
  25. ULONG g_NumPorts = 0;
  26. LARGE_INTEGER AcquirePortTimeout;
  27. UNICODE_STRING RegistryPath = {0,0,0};
  28. NTSTATUS
  29. ParRegisterForParportRemovalRelations(
  30. IN PDEVICE_EXTENSION Extension
  31. )
  32. {
  33. NTSTATUS status;
  34. PARPORT_REMOVAL_RELATIONS pptRemovalRelations;
  35. PDEVICE_OBJECT portDevObj = Extension->PortDeviceObject;
  36. if( Extension->RegForPptRemovalRelations ) {
  37. // already registered - don't do it again
  38. return STATUS_SUCCESS;
  39. }
  40. pptRemovalRelations.DeviceObject = Extension->DeviceObject;
  41. pptRemovalRelations.Flags = 0;
  42. pptRemovalRelations.DeviceName = &Extension->ClassName;
  43. status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_REGISTER_FOR_REMOVAL_RELATIONS, portDevObj,
  44. &pptRemovalRelations, sizeof(PARPORT_REMOVAL_RELATIONS),
  45. NULL, 0, NULL);
  46. if( NT_SUCCESS( status ) ) {
  47. Extension->RegForPptRemovalRelations = TRUE;
  48. }
  49. return status;
  50. }
  51. NTSTATUS
  52. ParUnregisterForParportRemovalRelations(
  53. IN PDEVICE_EXTENSION Extension
  54. )
  55. {
  56. NTSTATUS status;
  57. PARPORT_REMOVAL_RELATIONS pptRemovalRelations;
  58. PDEVICE_OBJECT portDevObj = Extension->PortDeviceObject;
  59. if( !Extension->RegForPptRemovalRelations ) {
  60. // we're not registered - don't try to unregister
  61. return STATUS_SUCCESS;
  62. }
  63. if( Extension->ParPortDeviceGone ) {
  64. // ParPort device is already gone - probably surprise removed
  65. // - don't try to send IOCTL or we will likely bugcheck
  66. return STATUS_SUCCESS;
  67. }
  68. pptRemovalRelations.DeviceObject = Extension->DeviceObject;
  69. pptRemovalRelations.Flags = 0;
  70. pptRemovalRelations.DeviceName = &Extension->ClassName;
  71. status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_UNREGISTER_FOR_REMOVAL_RELATIONS, portDevObj,
  72. &pptRemovalRelations, sizeof(PARPORT_REMOVAL_RELATIONS),
  73. NULL, 0, NULL);
  74. if( NT_SUCCESS( status ) ) {
  75. Extension->RegForPptRemovalRelations = FALSE;
  76. }
  77. return status;
  78. }
  79. PCHAR
  80. Par3QueryDeviceId(
  81. IN PDEVICE_EXTENSION Extension,
  82. OUT PCHAR CallerDeviceIdBuffer, OPTIONAL
  83. IN ULONG CallerBufferSize,
  84. OUT PULONG DeviceIdSize,
  85. IN BOOLEAN bReturnRawString, // TRUE == include the 2 size bytes in the returned string
  86. // FALSE == discard the 2 size bytes
  87. IN BOOLEAN bBuildStlDeviceId
  88. )
  89. /*++
  90. This is the replacement function for SppQueryDeviceId.
  91. This function uses the caller supplied buffer if the supplied buffer
  92. is large enough to hold the device id. Otherwise, a buffer is
  93. allocated from paged pool to hold the device ID and a pointer to
  94. the allocated buffer is returned to the caller. The caller determines
  95. whether a buffer was allocated by comparing the returned PCHAR with
  96. the DeviceIdBuffer parameter passed to this function. A NULL return
  97. value indicates that an error occurred.
  98. *** this function assumes that the caller has already acquired
  99. the port (and selected the device if needed in the case
  100. of a 1284.3 daisy chain device).
  101. *** If this function returns a pointer to a paged pool allocation then
  102. the caller is responsible for freeing the buffer when it is no
  103. longer needed.
  104. --*/
  105. {
  106. PUCHAR Controller = Extension->Controller;
  107. NTSTATUS Status;
  108. UCHAR idSizeBuffer[2];
  109. ULONG bytesToRead;
  110. ULONG bytesRead = 0;
  111. USHORT deviceIdSize;
  112. USHORT deviceIdBufferSize;
  113. PCHAR deviceIdBuffer;
  114. PCHAR readPtr;
  115. BOOLEAN allocatedBuffer = FALSE;
  116. ParDumpV( ("Enter pnp::Par3QueryDeviceId: Controller=%x\n", Controller) );
  117. if( TRUE == bBuildStlDeviceId ) {
  118. // if this is a legacy stl, forward call to special handler
  119. return ParStlQueryStlDeviceId(Extension,
  120. CallerDeviceIdBuffer, CallerBufferSize,
  121. DeviceIdSize, bReturnRawString);
  122. }
  123. if( Extension->Ieee1284_3DeviceId == DOT3_LEGACY_ZIP_ID ) {
  124. // if this is a legacy Zip, forward call to special handler
  125. return Par3QueryLegacyZipDeviceId(Extension,
  126. CallerDeviceIdBuffer, CallerBufferSize,
  127. DeviceIdSize, bReturnRawString);
  128. }
  129. //
  130. // Take a 40ms nap - there is at least one printer that can't handle
  131. // back to back 1284 device ID queries without a minimum 20-30ms delay
  132. // between the queries which breaks PnP'ing the printer
  133. //
  134. if( KeGetCurrentIrql() == PASSIVE_LEVEL ) {
  135. LARGE_INTEGER delay;
  136. delay.QuadPart = - 10 * 1000 * 40; // 40 ms
  137. KeDelayExecutionThread( KernelMode, FALSE, &delay );
  138. }
  139. *DeviceIdSize = 0;
  140. //
  141. // If we are currently connected to the peripheral via any 1284 mode
  142. // other than Compatibility/Spp mode (which does not require an IEEE
  143. // negotiation), we must first terminate the current mode/connection.
  144. //
  145. // dvdf - RMT - what if we are connected in a reverse mode?
  146. //
  147. if( (Extension->Connected) && (afpForward[Extension->IdxForwardProtocol].fnDisconnect) ) {
  148. afpForward[Extension->IdxForwardProtocol].fnDisconnect (Extension);
  149. }
  150. //
  151. // Negotiate the peripheral into nibble device id mode.
  152. //
  153. Status = ParEnterNibbleMode(Extension, REQUEST_DEVICE_ID);
  154. if( !NT_SUCCESS(Status) ) {
  155. ParDumpV( ("pnp::Par3QueryDeviceId: call to ParEnterNibbleMode FAILED\n") );
  156. ParTerminateNibbleMode(Extension);
  157. return NULL;
  158. }
  159. //
  160. // Read first two bytes to get the total (including the 2 size bytes) size
  161. // of the Device Id string.
  162. //
  163. bytesToRead = 2;
  164. Status = ParNibbleModeRead(Extension, idSizeBuffer, bytesToRead, &bytesRead);
  165. if( !NT_SUCCESS( Status ) || ( bytesRead != bytesToRead ) ) {
  166. ParDumpV( ("pnp::Par3QueryDeviceId: read of DeviceID size FAILED\n") );
  167. return NULL;
  168. }
  169. //
  170. // Compute size of DeviceId string (including the 2 byte size prefix)
  171. //
  172. deviceIdSize = (USHORT)( idSizeBuffer[0]*0x100 + idSizeBuffer[1] );
  173. ParDumpV( ("pnp::Par3QueryDeviceId: DeviceIdSize (including 2 size bytes) reported as %d\n", deviceIdSize) );
  174. //
  175. // Allocate a buffer to hold the DeviceId string and read the DeviceId into it.
  176. //
  177. if( bReturnRawString ) {
  178. //
  179. // Caller wants the raw string including the 2 size bytes
  180. //
  181. *DeviceIdSize = deviceIdSize;
  182. deviceIdBufferSize = (USHORT)(deviceIdSize + sizeof(CHAR)); // ID size + ID + terminating NULL
  183. } else {
  184. //
  185. // Caller does not want the 2 byte size prefix
  186. //
  187. *DeviceIdSize = deviceIdSize - 2*sizeof(CHAR);
  188. deviceIdBufferSize = (USHORT)(deviceIdSize - 2*sizeof(CHAR) + sizeof(CHAR)); // ID + terminating NULL
  189. }
  190. //
  191. // If caller's buffer is large enough use it, otherwise allocate a buffer
  192. // to hold the device ID
  193. //
  194. if( CallerDeviceIdBuffer && (CallerBufferSize >= deviceIdBufferSize) ) {
  195. //
  196. // Use caller's buffer - *** NOTE: we are creating an alias for the caller buffer
  197. //
  198. deviceIdBuffer = CallerDeviceIdBuffer;
  199. ParDumpV( ("pnp::Par3QueryDeviceId: using Caller supplied buffer\n") );
  200. } else {
  201. //
  202. // Either caller did not supply a buffer or supplied a buffer that is not
  203. // large enough to hold the device ID, so allocate a buffer.
  204. //
  205. ParDumpV( ("pnp::Par3QueryDeviceId: Caller's Buffer TOO_SMALL - CallerBufferSize= %d, deviceIdBufferSize= %d\n",
  206. CallerBufferSize, deviceIdBufferSize) );
  207. ParDumpV( ("pnp::Par3QueryDeviceId: will allocate and return ptr to buffer\n") );
  208. deviceIdBuffer = (PCHAR)ExAllocatePool(PagedPool, deviceIdBufferSize);
  209. if( !deviceIdBuffer ) {
  210. ParDumpV( ("pnp::Par3QueryDeviceId: ExAllocatePool FAILED\n") );
  211. return NULL;
  212. }
  213. allocatedBuffer = TRUE; // note that we allocated our own buffer rather than using caller's buffer
  214. }
  215. //
  216. // NULL out the ID buffer to be safe
  217. //
  218. RtlZeroMemory( deviceIdBuffer, deviceIdBufferSize );
  219. //
  220. // Does the caller want the 2 byte size prefix?
  221. //
  222. if( bReturnRawString ) {
  223. //
  224. // Yes, caller wants the size prefix. Copy prefix to buffer to return.
  225. //
  226. *(deviceIdBuffer+0) = idSizeBuffer[0];
  227. *(deviceIdBuffer+1) = idSizeBuffer[1];
  228. readPtr = deviceIdBuffer + 2;
  229. } else {
  230. //
  231. // No, discard size prefix
  232. //
  233. readPtr = deviceIdBuffer;
  234. }
  235. //
  236. // Read remainder of DeviceId from device
  237. //
  238. bytesToRead = deviceIdSize - 2; // already have the 2 size bytes
  239. Status = ParNibbleModeRead(Extension, readPtr, bytesToRead, &bytesRead);
  240. ParTerminateNibbleMode( Extension );
  241. WRITE_PORT_UCHAR(Controller + DCR_OFFSET, DCR_NEUTRAL);
  242. if( !NT_SUCCESS(Status) || (bytesRead < 1) ) {
  243. if( allocatedBuffer ) {
  244. // we're using our own allocated buffer rather than a caller supplied buffer - free it
  245. ParDump2(PARERRORS, ("Par3QueryDeviceId:: read of DeviceId FAILED - discarding buffer\n") );
  246. ExFreePool( deviceIdBuffer );
  247. }
  248. return NULL;
  249. }
  250. if ( bytesRead < bytesToRead ) {
  251. //
  252. // Device likely reported incorrect value for IEEE 1284 Device ID length
  253. //
  254. // This spew is on by default in checked builds to try to get
  255. // a feel for how many types of devices are broken in this way
  256. //
  257. DbgPrint(("pnp::Par3QueryDeviceId - ID shorter than expected\n") );
  258. }
  259. return deviceIdBuffer;
  260. }
  261. NTSTATUS
  262. ParSynchCompletionRoutine(
  263. IN PDEVICE_OBJECT DeviceObject,
  264. IN PIRP Irp,
  265. IN PKEVENT Event
  266. )
  267. /*++
  268. Routine Description:
  269. This routine is for use with synchronous IRP processing.
  270. All it does is signal an event, so the driver knows it
  271. can continue.
  272. Arguments:
  273. DriverObject - Pointer to driver object created by system.
  274. Irp - Irp that just completed
  275. Event - Event we'll signal to say Irp is done
  276. Return Value:
  277. None.
  278. --*/
  279. {
  280. UNREFERENCED_PARAMETER( DeviceObject );
  281. UNREFERENCED_PARAMETER( Irp );
  282. KeSetEvent(Event, 0, FALSE);
  283. return STATUS_MORE_PROCESSING_REQUIRED;
  284. }
  285. VOID
  286. ParCheckParameters(
  287. IN OUT PDEVICE_EXTENSION Extension
  288. )
  289. /*++
  290. Routine Description:
  291. This routine reads the parameters section of the registry and modifies
  292. the device extension as specified by the parameters.
  293. Arguments:
  294. RegistryPath - Supplies the registry path.
  295. Extension - Supplies the device extension.
  296. Return Value:
  297. None.
  298. --*/
  299. {
  300. RTL_QUERY_REGISTRY_TABLE ParamTable[4];
  301. ULONG UsePIWriteLoop;
  302. ULONG UseNT35Priority;
  303. ULONG Zero = 0;
  304. NTSTATUS Status;
  305. HANDLE hRegistry;
  306. ParDump(PARDUMP_VERBOSE_MAX,
  307. ("PARALLEL: "
  308. "Enter ParCheckParameters(...)\n") );
  309. if (Extension->PhysicalDeviceObject) {
  310. Status = IoOpenDeviceRegistryKey (Extension->PhysicalDeviceObject,
  311. PLUGPLAY_REGKEY_DRIVER,
  312. STANDARD_RIGHTS_ALL,
  313. &hRegistry);
  314. if (NT_SUCCESS(Status)) {
  315. RtlZeroMemory(ParamTable, sizeof(ParamTable));
  316. ParamTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
  317. ParamTable[0].Name = (PWSTR)L"UsePIWriteLoop";
  318. ParamTable[0].EntryContext = &UsePIWriteLoop;
  319. ParamTable[0].DefaultType = REG_DWORD;
  320. ParamTable[0].DefaultData = &Zero;
  321. ParamTable[0].DefaultLength = sizeof(ULONG);
  322. ParamTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
  323. ParamTable[1].Name = (PWSTR)L"UseNT35Priority";
  324. ParamTable[1].EntryContext = &UseNT35Priority;
  325. ParamTable[1].DefaultType = REG_DWORD;
  326. ParamTable[1].DefaultData = &Zero;
  327. ParamTable[1].DefaultLength = sizeof(ULONG);
  328. ParamTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
  329. ParamTable[2].Name = (PWSTR)L"InitializationTimeout";
  330. ParamTable[2].EntryContext = &(Extension->InitializationTimeout);
  331. ParamTable[2].DefaultType = REG_DWORD;
  332. ParamTable[2].DefaultData = &Zero;
  333. ParamTable[2].DefaultLength = sizeof(ULONG);
  334. Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
  335. hRegistry, ParamTable, NULL, NULL);
  336. if (NT_SUCCESS(Status)) {
  337. if(UsePIWriteLoop) {
  338. Extension->UsePIWriteLoop = TRUE;
  339. }
  340. if(UseNT35Priority) {
  341. Extension->UseNT35Priority = TRUE;
  342. }
  343. if(Extension->InitializationTimeout == 0) {
  344. Extension->InitializationTimeout = 15;
  345. }
  346. }
  347. } else {
  348. Extension->InitializationTimeout = 15;
  349. }
  350. ZwClose (hRegistry);
  351. } else {
  352. Extension->InitializationTimeout = 15;
  353. }
  354. }
  355. BOOLEAN
  356. String2Num(
  357. IN OUT PUCHAR *lpp_Str,
  358. IN CHAR c,
  359. OUT ULONG *num
  360. )
  361. {
  362. PUCHAR string = *lpp_Str;
  363. int cc;
  364. int cnt = 0;
  365. ParDump2(PARINFO, ("String2Num. string [%s]\n", string) );
  366. *num = 0;
  367. if (!*lpp_Str)
  368. {
  369. ParDump2(PARINFO, ("String2Num. Null String\n") );
  370. *num = 0;
  371. return FALSE;
  372. }
  373. // At this point, we should have a string that is a
  374. // positive hex value. I will not be checking for
  375. // validity of the string. If peripheral handed me a
  376. // bogus value then I'm gonna make their life
  377. // miserable.
  378. String2Num_Start:
  379. cc = (int)(unsigned char)**lpp_Str;
  380. if (cc >= '0' && cc <= '9')
  381. {
  382. *num = 16 * *num + (cc - '0'); /* accumulate digit */
  383. }
  384. else if (cc >= 'A' && cc <= 'F')
  385. {
  386. *num = 16 * *num + (cc - 55); /* accumulate digit */
  387. }
  388. else if (cc >= 'a' && cc <= 'f')
  389. {
  390. *num = 16 * *num + (cc - 87); /* accumulate digit */
  391. }
  392. else if (cc == c || cc == 0)
  393. {
  394. ParDump2(PARINFO, ("String2Num. Delimeter found num [%x]\n", *num));
  395. *lpp_Str = 0;
  396. return TRUE;
  397. }
  398. else if (cc == 'y' || cc == 'Y')
  399. {
  400. ParDump2(PARINFO, ("String2Num. Special case 'y' hit\n") );
  401. *lpp_Str = 0;
  402. *num = -1; /* Special case */
  403. return FALSE;
  404. }
  405. else
  406. {
  407. ParDump2(PARINFO, ("String2Num. Dunno\n") );
  408. *lpp_Str = 0;
  409. *num = 0; /* It's all messed up */
  410. return FALSE;
  411. }
  412. ParDump2(PARINFO, ("String2Num. num [%x]\n", *num) );
  413. (*lpp_Str)++;
  414. if (cnt++ > 100)
  415. {
  416. // If our string is this large, then I'm gonna assume somethings
  417. // wrong
  418. ParDump2(PARINFO, ("String2Num. String too long\n") );
  419. goto String2Num_End;
  420. }
  421. goto String2Num_Start;
  422. String2Num_End:
  423. ParDump2(PARINFO, ("String2Num. Somethings wrong with String\n") );
  424. *num = 0;
  425. return FALSE;
  426. }
  427. UCHAR
  428. StringCountValues(
  429. IN PCHAR string,
  430. IN CHAR delimeter
  431. )
  432. {
  433. PUCHAR lpKey = (PUCHAR)string;
  434. UCHAR cnt = 1;
  435. if(!string) {
  436. return 0;
  437. }
  438. while(*lpKey) {
  439. if( *lpKey==delimeter ) {
  440. ++cnt;
  441. }
  442. lpKey++;
  443. }
  444. return cnt;
  445. }
  446. PUCHAR
  447. StringChr(
  448. IN PCHAR string,
  449. IN CHAR c
  450. )
  451. {
  452. if(!string) {
  453. return(NULL);
  454. }
  455. while(*string) {
  456. if( *string==c ) {
  457. return (PUCHAR)string;
  458. }
  459. string++;
  460. }
  461. return NULL;
  462. }
  463. VOID
  464. StringSubst(
  465. IN PUCHAR lpS,
  466. IN UCHAR chTargetChar,
  467. IN UCHAR chReplacementChar,
  468. IN USHORT cbS
  469. )
  470. {
  471. USHORT iCnt = 0;
  472. while ((lpS != '\0') && (iCnt++ < cbS))
  473. if (*lpS == chTargetChar)
  474. *lpS++ = chReplacementChar;
  475. else
  476. ++lpS;
  477. }
  478. VOID
  479. ParFixupDeviceId(
  480. IN OUT PUCHAR DeviceId
  481. )
  482. /*++
  483. Routine Description:
  484. This routine parses the NULL terminated string and replaces any invalid
  485. characters with an underscore character.
  486. Invalid characters are:
  487. c <= 0x20 (' ')
  488. c > 0x7F
  489. c == 0x2C (',')
  490. Arguments:
  491. DeviceId - specifies a device id string (or part of one), must be
  492. null-terminated.
  493. Return Value:
  494. None.
  495. --*/
  496. {
  497. PUCHAR p;
  498. for( p = DeviceId; *p; ++p ) {
  499. if( (*p <= ' ') || (*p > (UCHAR)0x7F) || (*p == ',') ) {
  500. *p = '_';
  501. }
  502. }
  503. }
  504. VOID
  505. ParDetectDot3DataLink(
  506. IN PDEVICE_EXTENSION Extension,
  507. IN PUCHAR DeviceId
  508. )
  509. {
  510. PUCHAR DOT3DL = NULL; // 1284.3 Data Link Channels
  511. PUCHAR DOT3C = NULL; // 1284.3 Data Link Services
  512. PUCHAR DOT4DL = NULL; // 1284.4 Data Link for peripherals that were
  513. // implemented prior to 1284.3
  514. PUCHAR CMDField = NULL; // The command field for parsing legacy MLC
  515. PUCHAR DOT3M = NULL; // 1284 physical layer modes that will break this device
  516. ParDump2(PARDUMP_PNP_DL, ("ParDetectDot3DataLink: DeviceId [%s]\n", DeviceId) );
  517. ParDot3ParseDevId(&DOT3DL, &DOT3C, &CMDField, &DOT4DL, &DOT3M, DeviceId);
  518. ParDot3ParseModes(Extension,DOT3M);
  519. if (DOT4DL)
  520. {
  521. ParDump2(PARDUMP_PNP_DL, ("1284.4 with MLC Data Link Detected. DOT4DL [%s]\n", DOT4DL) );
  522. ParDot4CreateObject(Extension, DOT4DL);
  523. }
  524. else if (DOT3DL)
  525. {
  526. ParDump2(PARDUMP_PNP_DL, ("1284.3 Data Link Detected DL:[%s] C:[%s]\n", DOT3DL, DOT3C) );
  527. ParDot3CreateObject(Extension, DOT3DL, DOT3C);
  528. }
  529. else if (CMDField)
  530. {
  531. ParDump2(PARDUMP_PNP_DL, ("MLC Data Link Detected. MLC [%s]\n", CMDField) );
  532. ParMLCCreateObject(Extension, CMDField);
  533. }
  534. #if DBG
  535. else
  536. {
  537. ParDump2(PARDUMP_PNP_DL, ("No Data Link Detected\n") );
  538. }
  539. #endif
  540. }
  541. VOID
  542. ParDot3ParseDevId(
  543. PUCHAR *lpp_DL,
  544. PUCHAR *lpp_C,
  545. PUCHAR *lpp_CMD,
  546. PUCHAR *lpp_4DL,
  547. PUCHAR *lpp_M,
  548. PUCHAR lpDeviceID
  549. )
  550. {
  551. PUCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
  552. PUCHAR lpValue; // Pointer to the Key's value
  553. USHORT wKeyLength; // Length for the Key (for stringcmps)
  554. // While there are still keys to look at.
  555. ParDump(PARDUMP_PNP_DL,
  556. ("PARALLEL: "
  557. "Enter ParDot3ParseDevId(...)\n") );
  558. while (lpKey != NULL)
  559. {
  560. while (*lpKey == ' ')
  561. ++lpKey;
  562. // Is there a terminating COLON character for the current key?
  563. if (!(lpValue = StringChr((PCHAR)lpKey, ':')) ) {
  564. // N: OOPS, somthing wrong with the Device ID
  565. return;
  566. }
  567. // The actual start of the Key value is one past the COLON
  568. ++lpValue;
  569. //
  570. // Compute the Key length for Comparison, including the COLON
  571. // which will serve as a terminator
  572. //
  573. wKeyLength = (USHORT)(lpValue - lpKey);
  574. //
  575. // Compare the Key to the Know quantities. To speed up the comparison
  576. // a Check is made on the first character first, to reduce the number
  577. // of strings to compare against.
  578. // If a match is found, the appropriate lpp parameter is set to the
  579. // key's value, and the terminating SEMICOLON is converted to a NULL
  580. // In all cases lpKey is advanced to the next key if there is one.
  581. //
  582. switch (*lpKey) {
  583. case '1':
  584. // Look for DOT3 Datalink
  585. if((RtlCompareMemory(lpKey, "1284.4DL:", wKeyLength)==9))
  586. {
  587. *lpp_4DL = lpValue;
  588. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=NULL)
  589. {
  590. *lpKey = '\0';
  591. ++lpKey;
  592. }
  593. } else if((RtlCompareMemory(lpKey, "1284.3DL:", wKeyLength)==9))
  594. {
  595. *lpp_DL = lpValue;
  596. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=NULL)
  597. {
  598. *lpKey = '\0';
  599. ++lpKey;
  600. }
  601. } else if((RtlCompareMemory(lpKey, "1284.3C:", wKeyLength)==8))
  602. {
  603. *lpp_C = lpValue;
  604. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  605. *lpKey = '\0';
  606. ++lpKey;
  607. }
  608. } else if((RtlCompareMemory(lpKey, "1284.3M:", wKeyLength)==8))
  609. {
  610. *lpp_M = lpValue;
  611. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  612. *lpKey = '\0';
  613. ++lpKey;
  614. }
  615. } else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  616. *lpKey = '\0';
  617. ++lpKey;
  618. }
  619. break;
  620. case '.':
  621. // Look for for .3 extras
  622. if ((RtlCompareMemory(lpKey, ".3C:", wKeyLength)==4) ) {
  623. *lpp_C = lpValue;
  624. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  625. *lpKey = '\0';
  626. ++lpKey;
  627. }
  628. } else if ((RtlCompareMemory(lpKey, ".3M:", wKeyLength)==4) ) {
  629. *lpp_M = lpValue;
  630. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  631. *lpKey = '\0';
  632. ++lpKey;
  633. }
  634. } else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  635. *lpKey = '\0';
  636. ++lpKey;
  637. }
  638. break;
  639. case 'C':
  640. // Look for MLC Datalink
  641. if ((RtlCompareMemory(lpKey, "CMD:", wKeyLength)==4) ) {
  642. *lpp_CMD = lpValue;
  643. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  644. *lpKey = '\0';
  645. ++lpKey;
  646. }
  647. } else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  648. *lpKey = '\0';
  649. ++lpKey;
  650. }
  651. break;
  652. default:
  653. // The key is uninteresting. Go to the next Key
  654. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  655. *lpKey = '\0';
  656. ++lpKey;
  657. }
  658. break;
  659. }
  660. }
  661. }
  662. PUCHAR
  663. ParQueryDeviceId(
  664. IN PDEVICE_EXTENSION Extension
  665. )
  666. /*++
  667. Routine Description:
  668. try to read a device ID from a device
  669. *** this function assumes that the caller has already acquired
  670. the port (and selected the device if needed).
  671. *** on success, this function returns a pointer to allocated pool
  672. which the caller is responsible for freeing when it is no
  673. longer needed
  674. Arguments:
  675. Extension - used to find the Controller
  676. Return Value:
  677. PUCHAR - points to DeviceIdString on success
  678. NULL - if failure
  679. --*/
  680. {
  681. PUCHAR Controller = Extension->Controller;
  682. NTSTATUS Status;
  683. UCHAR SizeBuf[2];
  684. ULONG NumBytes = 0;
  685. USHORT Size;
  686. PUCHAR deviceId;
  687. if ((Extension->Connected) &&
  688. (afpForward[Extension->IdxForwardProtocol].fnDisconnect)) {
  689. afpForward[Extension->IdxForwardProtocol].fnDisconnect (Extension);
  690. }
  691. //
  692. // negotiate the peripheral into nibble mode/device id request.
  693. //
  694. Status = ParEnterNibbleMode(Extension, TRUE);
  695. if (!NT_SUCCESS(Status)) {
  696. ParDumpV( ("ParQueryDeviceId() - ParEnterNibbleMode FAILed\n") );
  697. return NULL;
  698. }
  699. // read the Device Id string size (encoded in first 2 bytes)
  700. // - reported size includes the 2 size bytes
  701. Status = ParNibbleModeRead(Extension, SizeBuf, 2, &NumBytes);
  702. if( !NT_SUCCESS(Status) || ( NT_SUCCESS(Status) && NumBytes != 2 ) ) {
  703. // read of ID string size failed
  704. ParDumpV( ("ParQueryDeviceId() - Read of ID string size FAILed\n") );
  705. ParTerminateNibbleMode(Extension);
  706. WRITE_PORT_UCHAR(Controller + DCR_OFFSET, DCR_NEUTRAL);
  707. return NULL;
  708. }
  709. // we have the deviceId size
  710. Size = (USHORT)( SizeBuf[0]*0x100 + SizeBuf[1] );
  711. // *DeviceIdSize = Size - sizeof(USHORT);
  712. ParDumpV( ("DeviceIdSize reported as %d, attempting to read DeviceId\n", Size - sizeof(USHORT)) );
  713. // allocate a buffer to hold device ID
  714. deviceId = ExAllocatePool(PagedPool, Size);
  715. if( !deviceId ) {
  716. ParDumpV( ("ParQueryDeviceId() - unable to allocate buffer to hold ID\n") );
  717. ParTerminateNibbleMode(Extension);
  718. WRITE_PORT_UCHAR(Controller + DCR_OFFSET, DCR_NEUTRAL);
  719. ExFreePool(deviceId);
  720. return NULL;
  721. }
  722. RtlZeroMemory(deviceId, Size);
  723. // read the device ID
  724. Status = ParNibbleModeRead(Extension, deviceId, Size - sizeof(USHORT), &NumBytes);
  725. if ( !NT_SUCCESS(Status) || ( NT_SUCCESS(Status) && ( NumBytes != (Size - sizeof(USHORT)) ) ) ) {
  726. ParDumpV( ("ParQueryDeviceId() - FAIL in read of DeviceID\n") );
  727. ParTerminateNibbleMode(Extension);
  728. WRITE_PORT_UCHAR(Controller + DCR_OFFSET, DCR_NEUTRAL);
  729. ExFreePool(deviceId);
  730. return NULL;
  731. }
  732. ParDumpV( ("ParQueryDeviceId() - ID=<%s>\n", deviceId) );
  733. return deviceId;
  734. }
  735. VOID
  736. ParKillDeviceObject(
  737. PDEVICE_OBJECT DeviceObject
  738. )
  739. /*++
  740. Routine Description:
  741. Kill a ParClass ejected device object:
  742. - set the device extension state to indicate that we are
  743. in the process of going away so that we can fail
  744. IRPs as appropriate
  745. - remove symbolic link to the device object
  746. - close handle to ParPort FILE object
  747. - unregister PnP notification callbacks
  748. - free pool allocations
  749. - remove the device object from the ParClass FDO's list of
  750. ParClass ejected device objects
  751. - delete the device object
  752. *** This routine assumes that the caller holds FdoExtension->DevObjListMutex
  753. Arguments:
  754. DeviceObject - The device object we want to kill
  755. Return Value:
  756. NONE
  757. --*/
  758. {
  759. PDEVICE_EXTENSION Extension;
  760. if( !DeviceObject ) {
  761. // insurance against receiving a NULL pointer
  762. ParDumpV( ("ParKillDeviceObject(...): passed a NULL pointer, returning") );
  763. return;
  764. }
  765. Extension = DeviceObject->DeviceExtension;
  766. if( !Extension->IsPdo ) {
  767. // we only handle ParClass ejected device objects (PDOs and PODOs)
  768. ParDumpV( ("ParKillDeviceObject(...): DeviceObject passed is the FDO, bailing out") );
  769. return;
  770. }
  771. ParDumpV( ("ParKillDeviceObject(...): Killing Device Object: %x %wZ\n",
  772. DeviceObject, &Extension->SymbolicLinkName) );
  773. //
  774. // set the device extension state to indicate that death is
  775. // imminent so that we can fail IRPs as appropriate
  776. //
  777. Extension->DeviceStateFlags |= PAR_DEVICE_DELETE_PENDING;
  778. // Notify the data link so it can begin the cleanup process.
  779. ParDot3DestroyObject(Extension);
  780. //
  781. // remove symbolic link to the device object
  782. //
  783. if( Extension->CreatedSymbolicLink ) {
  784. NTSTATUS status;
  785. status = IoDeleteSymbolicLink( &Extension->SymbolicLinkName );
  786. if( !NT_SUCCESS(status) ) {
  787. ParDumpV( ("IoDeleteSymbolicLink FAILED for %wZ\n", &Extension->SymbolicLinkName) );
  788. }
  789. status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP,
  790. (PWSTR)L"PARALLEL PORTS",
  791. Extension->ClassName.Buffer);
  792. if( !NT_SUCCESS(status) ) {
  793. ParDumpV( ("RtlDeleteRegistryValue FAILED for PARALLEL PORTS%wZ->%wZ\n",
  794. &Extension->ClassName, &Extension->SymbolicLinkName) );
  795. }
  796. Extension->CreatedSymbolicLink = FALSE;
  797. }
  798. //
  799. // close handle to ParPort FILE object
  800. //
  801. if( Extension->PortDeviceFileObject ) {
  802. ObDereferenceObject( Extension->PortDeviceFileObject );
  803. Extension->PortDeviceFileObject = NULL;
  804. }
  805. //
  806. // unregister PnP notification callbacks
  807. //
  808. if( Extension->NotificationHandle ) {
  809. IoUnregisterPlugPlayNotification (Extension->NotificationHandle);
  810. Extension->NotificationHandle = NULL;
  811. }
  812. //
  813. // If this is a PODO that is registered for WMI, unregister now
  814. // (PDOs don't do this because they register/unregister during START/REMOVE)
  815. //
  816. if( ParIsPodo(DeviceObject) && Extension->PodoRegForWMI ) {
  817. ParWMIRegistrationControl( DeviceObject, WMIREG_ACTION_DEREGISTER );
  818. Extension->PodoRegForWMI = FALSE;
  819. }
  820. //
  821. // free pool allocations that hold our name strings
  822. //
  823. RtlFreeUnicodeString(&Extension->PortSymbolicLinkName);
  824. RtlFreeUnicodeString(&Extension->SymbolicLinkName);
  825. RtlFreeUnicodeString(&Extension->ClassName);
  826. //
  827. // if the device object is in the list of ParClass ejected device objects,
  828. // remove it from the list
  829. //
  830. {
  831. //
  832. // The head of the list is really "ParClassPdo" in the
  833. // ParClass FDO's device extension
  834. //
  835. PDEVICE_EXTENSION FdoExtension = Extension->ParClassFdo->DeviceExtension;
  836. PDEVICE_OBJECT currentDO = FdoExtension->ParClassPdo;
  837. PDEVICE_EXTENSION currentExt;
  838. if( !currentDO ) { // empty device object list
  839. goto objectNotInList;
  840. }
  841. currentExt = currentDO->DeviceExtension;
  842. if( currentDO == DeviceObject ) {
  843. //
  844. // device object that we're looking for is the
  845. // first in the list, so remove it
  846. //
  847. FdoExtension->ParClassPdo = currentExt->Next;
  848. currentExt->Next = NULL;
  849. } else {
  850. //
  851. // walk the list to find the device object that we're looking for
  852. //
  853. PDEVICE_OBJECT nextDO = currentExt->Next;
  854. PDEVICE_EXTENSION nextExt;
  855. if( !nextDO ) { // object not in list
  856. goto objectNotInList;
  857. }
  858. nextExt = nextDO->DeviceExtension;
  859. while( nextDO != DeviceObject ) {
  860. //
  861. // we haven't found the device object that we're looking for yet,
  862. // so advance our pointers
  863. //
  864. currentDO = nextDO;
  865. currentExt = nextExt;
  866. nextDO = currentExt->Next;
  867. if( !nextDO ) { // object not in list
  868. goto objectNotInList;
  869. }
  870. nextExt = nextDO->DeviceExtension;
  871. }
  872. //
  873. // found device object - remove it from the list
  874. //
  875. currentExt->Next = nextExt->Next;
  876. nextExt->Next = NULL;
  877. }
  878. }
  879. objectNotInList: // target for device object not in list
  880. //
  881. // delete the device object
  882. //
  883. if( !(Extension->DeviceStateFlags & PAR_DEVICE_DELETED) ) {
  884. // mark extension so that we don't call IoDeleteDevice twice
  885. Extension->DeviceStateFlags |= PAR_DEVICE_DELETED;
  886. IoDeleteDevice(DeviceObject);
  887. }
  888. }
  889. BOOLEAN
  890. ParDeviceExists(
  891. IN PDEVICE_EXTENSION Extension,
  892. IN BOOLEAN HavePortKeepPort
  893. )
  894. /*++
  895. Routine Description:
  896. Is the hardware associated with this Device Object still there?
  897. Query for the device's 1284 device ID string, extract the
  898. relevent information from the raw ID string, and compare
  899. this information with that stored in the device's extension.
  900. Note: This function returns FALSE on any error.
  901. Arguments:
  902. Extension - The device extension of the Device Object to check
  903. Return Value:
  904. TRUE - if the device is still there
  905. FALSE - otherwise
  906. --*/
  907. {
  908. NTSTATUS status;
  909. PCHAR buffer = NULL;
  910. ULONG bufferLength;
  911. UCHAR resultString[MAX_ID_SIZE];
  912. BOOLEAN boolResult;
  913. ParDumpV( ("Enter ParDeviceExists(...): %wZ\n", &Extension->SymbolicLinkName) );
  914. RtlZeroMemory(resultString, MAX_ID_SIZE);
  915. //
  916. // Select the 1284.3 daisy chain device
  917. //
  918. if( !ParSelectDevice(Extension, HavePortKeepPort) ) {
  919. return FALSE;
  920. };
  921. //
  922. // Query the DeviceId
  923. //
  924. if ( Extension->Ieee1284Flags & ( 1 << Extension->Ieee1284_3DeviceId ) ) {
  925. buffer = Par3QueryDeviceId(Extension, NULL, 0, &bufferLength, FALSE, TRUE);
  926. }
  927. else {
  928. buffer = Par3QueryDeviceId(Extension, NULL, 0, &bufferLength, FALSE, FALSE);
  929. }
  930. //
  931. // We no longer need access to the hardware, Deselect the 1284.3 daisy chain device
  932. //
  933. boolResult = ParDeselectDevice(Extension, HavePortKeepPort);
  934. ASSERT(TRUE == boolResult);
  935. // check if we got a device ID
  936. if( !buffer ) {
  937. ParDumpV( ("pnp::ParDeviceExists: Device gone (Par3QueryDeviceId returned NULL)\n") );
  938. return FALSE;
  939. }
  940. ParDumpP( ("pnp::ParDeviceExists: \"RAW\" ID string = <%s>\n", buffer) );
  941. // extract the part of the ID that we want from the raw string
  942. // returned by the hardware
  943. status = ParPnpGetId((PUCHAR)buffer, BusQueryDeviceID, (PUCHAR)resultString, NULL);
  944. // no longer needed
  945. ExFreePool(buffer);
  946. // were we able to extract the info that we want from the raw ID string?
  947. if( !NT_SUCCESS(status) ) {
  948. return FALSE;
  949. }
  950. // Does the ID that we just retrieved from the device match the one
  951. // that we previously saved in the device extension?
  952. if(0 != strcmp((const PCHAR)Extension->DeviceIdString, (const PCHAR)resultString)) {
  953. ParDumpP( ("pnp::ParDeviceExists: device <%s> on %wZ GONE - strcmp failed\n",
  954. resultString, &Extension->SymbolicLinkName) );
  955. ParDumpP( ("pnp::ParDeviceExists: existing device was <%s>\n",
  956. Extension->DeviceIdString) );
  957. return FALSE;
  958. }
  959. ParDumpP( ("pnp::ParDeviceExists: device <%s> on %wZ is STILL THERE\n",
  960. resultString, &Extension->SymbolicLinkName) );
  961. return TRUE;
  962. }
  963. NTSTATUS
  964. ParPnpGetId(
  965. IN PUCHAR DeviceIdString,
  966. IN ULONG Type,
  967. OUT PUCHAR resultString,
  968. OUT PUCHAR descriptionString OPTIONAL
  969. )
  970. /*
  971. Description:
  972. Creates Id's from the device id retrieved from the printer
  973. Parameters:
  974. DeviceId - String with raw device id
  975. Type - What of id we want as a result
  976. Id - requested id
  977. Return Value:
  978. NTSTATUS
  979. */
  980. {
  981. NTSTATUS status;
  982. USHORT checkSum=0; // A 16 bit check sum
  983. UCHAR nodeName[16] = "LPTENUM\\";
  984. // The following are used to generate sub-strings from the Device ID string
  985. // to get the DevNode name, and to update the registry
  986. PUCHAR MFG = NULL; // Manufacturer name
  987. PUCHAR MDL = NULL; // Model name
  988. PUCHAR CLS = NULL; // Class name
  989. PUCHAR AID = NULL; // Hardare ID
  990. PUCHAR CID = NULL; // Compatible IDs
  991. PUCHAR DES = NULL; // Device Description
  992. ParDump(PARDUMP_VERBOSE_MAX,
  993. ("PARALLEL: "
  994. "Enter ParPnpGetId(...)\n") );
  995. status = STATUS_SUCCESS;
  996. switch(Type) {
  997. case BusQueryDeviceID:
  998. // Extract the usefull fields from the DeviceID string. We want
  999. // MANUFACTURE (MFG):
  1000. // MODEL (MDL):
  1001. // AUTOMATIC ID (AID):
  1002. // COMPATIBLE ID (CID):
  1003. // DESCRIPTION (DES):
  1004. // CLASS (CLS):
  1005. ParPnpFindDeviceIdKeys(&MFG, &MDL, &CLS, &DES, &AID, &CID, DeviceIdString);
  1006. // Check to make sure we got MFG and MDL as absolute minimum fields. If not
  1007. // we cannot continue.
  1008. if (!MFG || !MDL)
  1009. {
  1010. status = STATUS_NOT_FOUND;
  1011. goto ParPnpGetId_Cleanup;
  1012. }
  1013. //
  1014. // Concatenate the provided MFG and MDL P1284 fields
  1015. // Checksum the entire MFG+MDL string
  1016. //
  1017. sprintf((PCHAR)resultString, "%s%s\0",MFG,MDL);
  1018. if (descriptionString) {
  1019. sprintf((PCHAR)descriptionString, "%s %s\0",MFG,MDL);
  1020. }
  1021. break;
  1022. case BusQueryHardwareIDs:
  1023. GetCheckSum((PUCHAR)DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
  1024. sprintf((PCHAR)resultString,"%s%.20s%04X",nodeName,DeviceIdString,checkSum);
  1025. break;
  1026. case BusQueryCompatibleIDs:
  1027. //
  1028. // return only 1 id
  1029. //
  1030. GetCheckSum(DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
  1031. sprintf((PCHAR)resultString,"%.20s%04X",DeviceIdString,checkSum);
  1032. break;
  1033. }
  1034. if (Type!=BusQueryDeviceID) {
  1035. //
  1036. // Convert and spaces in the Hardware ID to underscores
  1037. //
  1038. StringSubst (resultString, ' ', '_', (USHORT)strlen((const PCHAR)resultString));
  1039. }
  1040. ParPnpGetId_Cleanup:
  1041. return(status);
  1042. }
  1043. VOID
  1044. ParPnpFindDeviceIdKeys(
  1045. PUCHAR *lppMFG,
  1046. PUCHAR *lppMDL,
  1047. PUCHAR *lppCLS,
  1048. PUCHAR *lppDES,
  1049. PUCHAR *lppAID,
  1050. PUCHAR *lppCID,
  1051. PUCHAR lpDeviceID
  1052. )
  1053. /*
  1054. Description:
  1055. This function will parse a P1284 Device ID string looking for keys
  1056. of interest to the LPT enumerator. Got it from win95 lptenum
  1057. Parameters:
  1058. lppMFG Pointer to MFG string pointer
  1059. lppMDL Pointer to MDL string pointer
  1060. lppMDL Pointer to CLS string pointer
  1061. lppDES Pointer to DES string pointer
  1062. lppCIC Pointer to CID string pointer
  1063. lppAID Pointer to AID string pointer
  1064. lpDeviceID Pointer to the Device ID string
  1065. Return Value:
  1066. no return VALUE.
  1067. If found the lpp parameters are set to the approprate portions
  1068. of the DeviceID string, and they are NULL terminated.
  1069. The actual DeviceID string is used, and the lpp Parameters just
  1070. reference sections, with appropriate null thrown in.
  1071. */
  1072. {
  1073. PUCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
  1074. PUCHAR lpValue; // Pointer to the Key's value
  1075. USHORT wKeyLength; // Length for the Key (for stringcmps)
  1076. // While there are still keys to look at.
  1077. ParDump(PARDUMP_VERBOSE_MAX,
  1078. ("PARALLEL: "
  1079. "Enter ParPnpFindDeviceIdKeys(...)\n") );
  1080. while (lpKey != NULL)
  1081. {
  1082. while (*lpKey == ' ')
  1083. ++lpKey;
  1084. // Is there a terminating COLON character for the current key?
  1085. if (!(lpValue = StringChr((PCHAR)lpKey, ':')) ) {
  1086. // N: OOPS, somthing wrong with the Device ID
  1087. return;
  1088. }
  1089. // The actual start of the Key value is one past the COLON
  1090. ++lpValue;
  1091. //
  1092. // Compute the Key length for Comparison, including the COLON
  1093. // which will serve as a terminator
  1094. //
  1095. wKeyLength = (USHORT)(lpValue - lpKey);
  1096. //
  1097. // Compare the Key to the Know quantities. To speed up the comparison
  1098. // a Check is made on the first character first, to reduce the number
  1099. // of strings to compare against.
  1100. // If a match is found, the appropriate lpp parameter is set to the
  1101. // key's value, and the terminating SEMICOLON is converted to a NULL
  1102. // In all cases lpKey is advanced to the next key if there is one.
  1103. //
  1104. switch (*lpKey) {
  1105. case 'M':
  1106. // Look for MANUFACTURE (MFG) or MODEL (MDL)
  1107. if((RtlCompareMemory(lpKey, "MANUFACTURER", wKeyLength)>5) ||
  1108. (RtlCompareMemory(lpKey, "MFG", wKeyLength)==3) ) {
  1109. *lppMFG = lpValue;
  1110. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=NULL) {
  1111. *lpKey = '\0';
  1112. ++lpKey;
  1113. }
  1114. } else if((RtlCompareMemory(lpKey, "MODEL", wKeyLength)==5) ||
  1115. (RtlCompareMemory(lpKey, "MDL", wKeyLength)==3) ) {
  1116. *lppMDL = lpValue;
  1117. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1118. *lpKey = '\0';
  1119. ++lpKey;
  1120. }
  1121. } else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1122. *lpKey = '\0';
  1123. ++lpKey;
  1124. }
  1125. break;
  1126. case 'C':
  1127. // Look for CLASS (CLS)
  1128. if ((RtlCompareMemory(lpKey, "CLASS", wKeyLength)==5) ||
  1129. (RtlCompareMemory(lpKey, "CLS", wKeyLength)==3) ) {
  1130. *lppCLS = lpValue;
  1131. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1132. *lpKey = '\0';
  1133. ++lpKey;
  1134. }
  1135. } else if ((RtlCompareMemory(lpKey, "COMPATIBLEID", wKeyLength)>5) ||
  1136. (RtlCompareMemory(lpKey, "CID", wKeyLength)==3) ) {
  1137. *lppCID = lpValue;
  1138. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1139. *lpKey = '\0';
  1140. ++lpKey;
  1141. }
  1142. } else if ((lpKey = StringChr((PCHAR)lpValue,';'))!=0) {
  1143. *lpKey = '\0';
  1144. ++lpKey;
  1145. }
  1146. break;
  1147. case 'D':
  1148. // Look for DESCRIPTION (DES)
  1149. if(RtlCompareMemory(lpKey, "DESCRIPTION", wKeyLength) ||
  1150. RtlCompareMemory(lpKey, "DES", wKeyLength) ) {
  1151. *lppDES = lpValue;
  1152. if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1153. *lpKey = '\0';
  1154. ++lpKey;
  1155. }
  1156. } else if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1157. *lpKey = '\0';
  1158. ++lpKey;
  1159. }
  1160. break;
  1161. case 'A':
  1162. // Look for AUTOMATIC ID (AID)
  1163. if (RtlCompareMemory(lpKey, "AUTOMATICID", wKeyLength) ||
  1164. RtlCompareMemory(lpKey, "AID", wKeyLength) ) {
  1165. *lppAID = lpValue;
  1166. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1167. *lpKey = '\0';
  1168. ++lpKey;
  1169. }
  1170. } else if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1171. *lpKey = '\0';
  1172. ++lpKey;
  1173. }
  1174. break;
  1175. default:
  1176. // The key is uninteresting. Go to the next Key
  1177. if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
  1178. *lpKey = '\0';
  1179. ++lpKey;
  1180. }
  1181. break;
  1182. }
  1183. }
  1184. }
  1185. VOID
  1186. GetCheckSum(
  1187. PUCHAR Block,
  1188. USHORT Len,
  1189. PUSHORT CheckSum
  1190. )
  1191. {
  1192. USHORT i;
  1193. // UCHAR lrc;
  1194. USHORT crc = 0;
  1195. unsigned short crc16a[] = {
  1196. 0000000, 0140301, 0140601, 0000500,
  1197. 0141401, 0001700, 0001200, 0141101,
  1198. 0143001, 0003300, 0003600, 0143501,
  1199. 0002400, 0142701, 0142201, 0002100,
  1200. };
  1201. unsigned short crc16b[] = {
  1202. 0000000, 0146001, 0154001, 0012000,
  1203. 0170001, 0036000, 0024000, 0162001,
  1204. 0120001, 0066000, 0074000, 0132001,
  1205. 0050000, 0116001, 0104001, 0043000,
  1206. };
  1207. //
  1208. // Calculate CRC using tables.
  1209. //
  1210. UCHAR tmp;
  1211. for ( i=0; i<Len; i++) {
  1212. tmp = (UCHAR)(Block[i] ^ (UCHAR)crc);
  1213. crc = (USHORT)((crc >> 8) ^ crc16a[tmp & 0x0f] ^ crc16b[tmp >> 4]);
  1214. }
  1215. *CheckSum = crc;
  1216. }
  1217. //
  1218. // old pnpdone.c follows
  1219. //
  1220. NTSTATUS
  1221. ParParallelPnp (
  1222. IN PDEVICE_OBJECT pDeviceObject,
  1223. IN PIRP pIrp
  1224. )
  1225. /*++dvdf
  1226. Routine Description:
  1227. This is the dispatch routine for all PNP IRPs. It forwards the request
  1228. to the appropriate routine based on whether the DO is a PDO or FDO.
  1229. Arguments:
  1230. pDeviceObject - represents a parallel device
  1231. pIrp - PNP Irp
  1232. Return Value:
  1233. STATUS_SUCCESS - if successful.
  1234. STATUS_UNSUCCESSFUL - otherwise.
  1235. --*/
  1236. {
  1237. PDEVICE_EXTENSION extension = pDeviceObject->DeviceExtension;
  1238. if ( ((PDEVICE_EXTENSION)(pDeviceObject->DeviceExtension))->IsPdo ) {
  1239. ASSERT( extension->DeviceType && (PAR_DEVTYPE_PODO | PAR_DEVTYPE_PDO) );
  1240. return ParPdoParallelPnp (pDeviceObject, pIrp);
  1241. } else {
  1242. ASSERT( extension->DeviceType && PAR_DEVTYPE_FDO );
  1243. return ParFdoParallelPnp (pDeviceObject, pIrp);
  1244. }
  1245. }
  1246. NTSTATUS
  1247. ParAcquirePort(
  1248. IN PDEVICE_OBJECT PortDeviceObject,
  1249. IN PLARGE_INTEGER Timeout OPTIONAL
  1250. )
  1251. /*++dvdf
  1252. Routine Description:
  1253. This routine acquires the specified parallel port from the parallel
  1254. port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE.
  1255. Arguments:
  1256. PortDeviceObject - points to the ParPort device to be acquired
  1257. Return Value:
  1258. STATUS_SUCCESS - if the port was successfully acquired
  1259. !STATUS_SUCCESS - otherwise
  1260. --*/
  1261. {
  1262. LARGE_INTEGER localTimeout;
  1263. if( Timeout ) {
  1264. localTimeout = *Timeout; // caller specified
  1265. } else {
  1266. localTimeout = AcquirePortTimeout; // driver global variable default
  1267. }
  1268. return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE,
  1269. PortDeviceObject, NULL, 0, NULL, 0, &localTimeout);
  1270. }
  1271. NTSTATUS
  1272. ParReleasePort(
  1273. IN PDEVICE_OBJECT PortDeviceObject
  1274. )
  1275. /*++dvdf
  1276. Routine Description:
  1277. This routine releases the specified parallel port back to the the parallel
  1278. port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_FREE.
  1279. Arguments:
  1280. PortDeviceObject - points to the ParPort device to be released
  1281. Return Value:
  1282. STATUS_SUCCESS - if the port was successfully released
  1283. !STATUS_SUCCESS - otherwise
  1284. --*/
  1285. {
  1286. return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_FREE,
  1287. PortDeviceObject, NULL, 0, NULL, 0, NULL);
  1288. }
  1289. NTSTATUS
  1290. ParInit1284_3Bus(
  1291. IN PDEVICE_OBJECT PortDeviceObject
  1292. )
  1293. /*++dvdf
  1294. Routine Description:
  1295. This routine reinitializes the 1284.3 daisy chain "bus" via an
  1296. IOCTL_INTERNAL_INIT_1284_3_BUS sent to the ParPort device to
  1297. reinitialize.
  1298. Reinitializing the 1284.3 bus assigns addresses [0..3] to the
  1299. daisy chain devices based on their position in the 1284.3 daisy chain.
  1300. Address 0 is closest to the host port and address 3 is closest to the
  1301. end of the chain.
  1302. New devices must be assigned an address before they will respond to
  1303. 1284.3 SELECT and DESELECT commands.
  1304. A 1284.3 daisy chain device whose position in the 1284.3 daisy chain
  1305. has changed due to the addition or removal of another 1284.3 daisy
  1306. chain device between the existing device and the host port will be
  1307. assigned a new addresses based on its new position in the chain.
  1308. Preconditions:
  1309. Caller must have already Acquired the Port via ParAcquirePort()prior
  1310. to calling this function.
  1311. Preconditions:
  1312. Caller still owns the port after calling this function and is responsible
  1313. for freeing the port when it is no longer required via ParReleasePort().
  1314. Arguments:
  1315. PortDeviceObject - points to the ParPort that the device is connected to.
  1316. Return Value:
  1317. STATUS_SUCCESS - if the initialization was successful
  1318. !STATUS_SUCCESS - otherwise
  1319. --*/
  1320. {
  1321. return ParBuildSendInternalIoctl(IOCTL_INTERNAL_INIT_1284_3_BUS,
  1322. PortDeviceObject, NULL, 0, NULL, 0, NULL);
  1323. }
  1324. VOID
  1325. ParMarkPdoHardwareGone(
  1326. IN PDEVICE_EXTENSION Extension
  1327. )
  1328. /*++dvdf
  1329. Routine Description:
  1330. This routine is called to mark a device as "Hardware Gone", i.e., the
  1331. hardware associated with this device is no longer there.
  1332. - set DeviceState flag so that we know the device is no longer present
  1333. - mark extension so FDO no longer reports the device to PnP during BusRelation query
  1334. - delete symbolic link
  1335. - delete registry Parallelx -> LPTy mapping
  1336. Arguments:
  1337. Extension - points to the device extension of the device that has gone away
  1338. Return Value:
  1339. None.
  1340. --*/
  1341. {
  1342. //
  1343. // Mark our extension so that we know our hardware is gone.
  1344. //
  1345. Extension->DeviceStateFlags = PAR_DEVICE_HARDWARE_GONE;
  1346. //
  1347. // Mark our extension so that the ParClass FDO no longer reports us to PnP
  1348. // in response to QUERY_DEVICE_RELATIONS/BusRelations.
  1349. //
  1350. Extension->DeviceIdString[0] = 0;
  1351. //
  1352. // Cleanup Symbolic Link and Registry
  1353. //
  1354. if( Extension->CreatedSymbolicLink ) {
  1355. NTSTATUS status;
  1356. // Remove symbolic link NOW so that the name can be reused by another device
  1357. status = IoDeleteSymbolicLink( &Extension->SymbolicLinkName );
  1358. if( !NT_SUCCESS(status) ) {
  1359. ParDumpV( ("IoDeleteSymbolicLink FAILED for %wZ\n", &Extension->SymbolicLinkName) );
  1360. }
  1361. status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, (PWSTR)L"PARALLEL PORTS", Extension->ClassName.Buffer);
  1362. // Remove our Parallelx -> LPTy mapping from HKLM\HARDWARE\DEVICEMAP\PARALLEL PORTS
  1363. if( !NT_SUCCESS(status) ) {
  1364. ParDumpV( ("RtlDeleteRegistryValue FAILED for PARALLEL PORTS %wZ->%wZ\n",
  1365. &Extension->ClassName, &Extension->SymbolicLinkName) );
  1366. }
  1367. Extension->CreatedSymbolicLink = FALSE;
  1368. }
  1369. }
  1370. NTSTATUS
  1371. ParPnpNotifyTargetDeviceChange(
  1372. IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION pDeviceInterfaceChangeNotification,
  1373. IN PDEVICE_OBJECT pDeviceObject
  1374. )
  1375. /*++
  1376. Routine Description:
  1377. This routine is the PlugPlay notification callback routine that
  1378. gets called when our ParPort gets QUERY_REMOVE, REMOVE, or
  1379. REMOVE_CANCELLED.
  1380. Arguments:
  1381. pDeviceInterfaceChangeNotification - Structure defining the change.
  1382. pDeviceObject - The ParClass ejected device object
  1383. receiving the notification
  1384. (context passed when we registered
  1385. for notification)
  1386. Return Value:
  1387. STATUS_SUCCESS - always
  1388. --*/
  1389. {
  1390. PDEVICE_EXTENSION Extension = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
  1391. ParDump2(PARDUMP_PNP_PARPORT,
  1392. ("ParPnpNotifyTargetDeviceChange(...): "
  1393. "%x %wZ received PLUGPLAY Notification for ParPort Device\n",
  1394. pDeviceObject, &Extension->SymbolicLinkName) );
  1395. if(IsEqualGUID( (LPGUID)&(pDeviceInterfaceChangeNotification->Event),
  1396. (LPGUID)&GUID_TARGET_DEVICE_QUERY_REMOVE)) {
  1397. //
  1398. // Our ParPort is going to receive a QUERY_REMOVE
  1399. //
  1400. ParDump2(PARDUMP_PNP_PARPORT,
  1401. ("ParPnpNotifyTargetDeviceChange(...): Our ParPort will receive QUERY_REMOVE\n") );
  1402. ExAcquireFastMutex(&Extension->OpenCloseMutex);
  1403. if (Extension->OpenCloseRefCount > 0) {
  1404. //
  1405. // someone has an open handle to us, do nothing,
  1406. // Our ParPort should fail QUERY_REMOVE because we
  1407. // still have a handle to it
  1408. //
  1409. DDPnP1(("## TargetQueryRemoveNotification - %wZ - keep handle open\n",&Extension->SymbolicLinkName));
  1410. ParDump2(PARDUMP_PNP_PARPORT,
  1411. ("ParPnpNotifyTargetDeviceChange(...): Someone has an open handle to us, "
  1412. "KEEP our handle to ParPort\n") );
  1413. } else if(Extension->PortDeviceFileObject) {
  1414. //
  1415. // close our handle to ParPort to prevent us
  1416. // from blocking our ParPort from succeeding
  1417. // its QUERY_REMOVE
  1418. //
  1419. DDPnP1(("## TargetQueryRemoveNotification - %wZ - close handle\n",&Extension->SymbolicLinkName));
  1420. ParDump2(PARDUMP_PNP_PARPORT,
  1421. ("ParPnpNotifyTargetDeviceChange(...): no one has an open handle to us, "
  1422. "CLOSE our handle to ParPort\n") );
  1423. ObDereferenceObject(Extension->PortDeviceFileObject);
  1424. Extension->PortDeviceFileObject = NULL;
  1425. //
  1426. // Set DeviceStateFlags accordingly so we handle
  1427. // IRPs properly while waiting to see if our ParPort gets
  1428. // REMOVE or REMOVE_CANCELLED
  1429. //
  1430. // We expect to be deleted, our parport is in a remove pending state
  1431. // Extension->DeviceStateFlags |= PAR_DEVICE_DELETE_PENDING;
  1432. Extension->DeviceStateFlags |= PAR_DEVICE_PORT_REMOVE_PENDING;
  1433. }
  1434. ExReleaseFastMutex(&Extension->OpenCloseMutex);
  1435. } else if(IsEqualGUID( (LPGUID)&(pDeviceInterfaceChangeNotification->Event),
  1436. (LPGUID)&GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
  1437. DDPnP1(("## TargetRemoveCompleteNotification - %wZ\n",&Extension->SymbolicLinkName));
  1438. //
  1439. // Our ParPort is gone, clean up
  1440. //
  1441. Extension->ParPortDeviceGone = TRUE;
  1442. //
  1443. // First, clean up any worker thread
  1444. //
  1445. if(Extension->ThreadObjectPointer) {
  1446. // set the flag for the worker thread to kill itself
  1447. Extension->TimeToTerminateThread = TRUE;
  1448. // wake up the thread so it can kill itself
  1449. KeReleaseSemaphore(&Extension->RequestSemaphore, 0, 1, FALSE);
  1450. // allow thread to get past PauseEvent so it can kill self
  1451. KeSetEvent(&Extension->PauseEvent, 0, TRUE);
  1452. // wait for the thread to die
  1453. KeWaitForSingleObject(Extension->ThreadObjectPointer, UserRequest, KernelMode, FALSE, NULL);
  1454. // allow the system to release the thread object
  1455. ObDereferenceObject(Extension->ThreadObjectPointer);
  1456. // note that we no longer have a worker thread
  1457. Extension->ThreadObjectPointer = NULL;
  1458. }
  1459. if( Extension->DeviceIdString[0] == 0 ) {
  1460. // this is a PODO, PnP doesn't know about us, so kill self now
  1461. PDEVICE_EXTENSION FdoExtension = Extension->ParClassFdo->DeviceExtension;
  1462. ExAcquireFastMutex(&FdoExtension->DevObjListMutex);
  1463. ParKillDeviceObject(pDeviceObject);
  1464. ExReleaseFastMutex(&FdoExtension->DevObjListMutex);
  1465. } else {
  1466. // this is a PDO, note that our hardware is gone and wait for PnP system
  1467. // to send us a REMOVE
  1468. PDEVICE_EXTENSION FdoExtension;
  1469. Extension->DeviceStateFlags = PAR_DEVICE_HARDWARE_GONE;
  1470. Extension->DeviceIdString[0] = 0;
  1471. //
  1472. // remove symbolic link NOW in case the interface returns before PnP gets
  1473. // around to sending us a QUERY_DEVICE_RELATIONS/BusRelations followed by
  1474. // a REMOVE_DEVICE
  1475. //
  1476. if( Extension->CreatedSymbolicLink ) {
  1477. NTSTATUS status;
  1478. status = IoDeleteSymbolicLink( &Extension->SymbolicLinkName );
  1479. if( !NT_SUCCESS(status) ) {
  1480. ParDump2(PARDUMP_PNP_PARPORT,
  1481. ("IoDeleteSymbolicLink FAILED for %wZ\n", &Extension->SymbolicLinkName) );
  1482. }
  1483. status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, (PWSTR)L"PARALLEL PORTS", Extension->ClassName.Buffer);
  1484. if( !NT_SUCCESS(status) ) {
  1485. ParDump2(PARDUMP_PNP_PARPORT,
  1486. ("RtlDeleteRegistryValue FAILED for PARALLEL PORTS %wZ->%wZ\n",
  1487. &Extension->ClassName, &Extension->SymbolicLinkName) );
  1488. }
  1489. Extension->CreatedSymbolicLink = FALSE;
  1490. }
  1491. // tell PnP that the set of ParClass enumerated PDOs has changed
  1492. FdoExtension = (PDEVICE_EXTENSION)(Extension->ParClassFdo->DeviceExtension);
  1493. IoInvalidateDeviceRelations(FdoExtension->PhysicalDeviceObject, BusRelations);
  1494. }
  1495. } else if(IsEqualGUID( (LPGUID)&(pDeviceInterfaceChangeNotification->Event),
  1496. (LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED)) {
  1497. //
  1498. // Our ParPort is back online (REMOVE_CANCELLED)
  1499. //
  1500. DDPnP1(("## TargetRemoveCancelledNotification - %wZ\n",&Extension->SymbolicLinkName));
  1501. ParDump2(PARDUMP_PNP_PARPORT,
  1502. ("ParPnpNotifyTargetDeviceChange(...): Our ParPort completed a REMOVE_CANCELLED\n") );
  1503. ExAcquireFastMutex(&Extension->OpenCloseMutex);
  1504. if( !Extension->PortDeviceFileObject ) {
  1505. //
  1506. // we dropped our connection to our ParPort prior to
  1507. // our ParPort receiving QUERY_REMOVE, reestablish
  1508. // a FILE connection and resume operation
  1509. //
  1510. NTSTATUS status;
  1511. PFILE_OBJECT portDeviceFileObject;
  1512. PDEVICE_OBJECT portDeviceObject;
  1513. ParDump2(PARDUMP_PNP_PARPORT,
  1514. ("ParPnpNotifyTargetDeviceChange(...): reopening file against our ParPort\n") );
  1515. status = IoGetDeviceObjectPointer(&Extension->PortSymbolicLinkName,
  1516. STANDARD_RIGHTS_ALL,
  1517. &portDeviceFileObject,
  1518. &portDeviceObject);
  1519. if(NT_SUCCESS(status) && portDeviceFileObject && portDeviceObject) {
  1520. // save REFERENCED PFILE_OBJECT in our device extension
  1521. Extension->PortDeviceFileObject = portDeviceFileObject;
  1522. // our ParPort device object should not have changed
  1523. ASSERT(Extension->PortDeviceObject == portDeviceObject);
  1524. } else {
  1525. ParDump2(PARDUMP_PNP_PARPORT,
  1526. ("In ParPnpNotifyTargetDeviceChange(...): Unable to reopen FILE against our ParPort\n") );
  1527. //
  1528. // Unable to reestablish connection? Inconceivable!
  1529. //
  1530. ASSERT(FALSE);
  1531. }
  1532. }
  1533. //
  1534. // set DeviceStateFlags accordingly to resume processing IRPs
  1535. //
  1536. Extension->DeviceStateFlags &= ~PAR_DEVICE_PORT_REMOVE_PENDING;
  1537. ExReleaseFastMutex(&Extension->OpenCloseMutex);
  1538. } else {
  1539. ParDump2(PARDUMP_PNP_PARPORT,
  1540. ("In ParPnpNotifyTargetDeviceChange(...): Unrecognized GUID_TARGET_DEVICE type\n") );
  1541. }
  1542. return STATUS_SUCCESS;
  1543. }
  1544. //
  1545. // dvdf - former devobj.c follows
  1546. //
  1547. VOID
  1548. ParMakeClassNameFromNumber(
  1549. IN ULONG Number,
  1550. OUT PUNICODE_STRING ClassName
  1551. )
  1552. /*++dvdf
  1553. Routine Description:
  1554. This routine creates a ClassName for a ParClass PODO of the form:
  1555. L"\Device\ParallelN" where N is the wide string representation
  1556. of the 'Number' parameter.
  1557. Note: On success ClassName->Buffer points to allocated pool.
  1558. The caller is responsible for freeing this allocation when
  1559. it is no longer required.
  1560. Note: The returned ClassName->Buffer is UNICODE_NULL terminated.
  1561. Arguments:
  1562. Number - Supplies the number.
  1563. ClassName - returns classname for device object on success,
  1564. (ClassName->Buffer == NULL) indicates failure
  1565. Return Value:
  1566. None - caller determines success or failure by examining ClassName->Buffer.
  1567. --*/
  1568. {
  1569. NTSTATUS status;
  1570. UNICODE_STRING digits;
  1571. WCHAR digitsBuffer[10];
  1572. UNICODE_STRING prefix;
  1573. PAGED_CODE();
  1574. // initialize ClassName to failure state
  1575. RtlInitUnicodeString(ClassName, NULL);
  1576. // create prefix
  1577. RtlInitUnicodeString(&prefix, (PWSTR)L"\\Device\\Parallel");
  1578. // create suffix
  1579. digits.Length = 0;
  1580. digits.MaximumLength = sizeof(digitsBuffer);
  1581. digits.Buffer = digitsBuffer;
  1582. status = RtlIntegerToUnicodeString(Number, 10, &digits);
  1583. if ( !NT_SUCCESS(status) ) {
  1584. return;
  1585. }
  1586. // calculate required space, allocate paged pool, and zero buffer
  1587. ClassName->MaximumLength = (USHORT)(prefix.Length + digits.Length + sizeof(WCHAR));
  1588. ClassName->Buffer = ExAllocatePool(PagedPool, ClassName->MaximumLength);
  1589. if( !ClassName->Buffer ) {
  1590. // unable to allocate pool, set ClassName to failure state and return
  1591. RtlInitUnicodeString(ClassName, NULL);
  1592. return;
  1593. }
  1594. RtlZeroMemory(ClassName->Buffer, ClassName->MaximumLength);
  1595. // try to catenate prefix and suffix in buffer to form ClassName
  1596. status = RtlAppendUnicodeStringToString(ClassName, &prefix);
  1597. if( !NT_SUCCESS(status) ) {
  1598. // error on prefix, release buffer, set ClassName to error state
  1599. RtlFreeUnicodeString(ClassName);
  1600. } else {
  1601. // prefix ok, try appending suffix
  1602. status = RtlAppendUnicodeStringToString(ClassName, &digits);
  1603. if( !NT_SUCCESS(status) ) {
  1604. // error on suffix, release buffer, set ClassName to failure state
  1605. RtlFreeUnicodeString(ClassName);
  1606. }
  1607. }
  1608. return;
  1609. }
  1610. VOID
  1611. ParMakeDotClassNameFromBaseClassName(
  1612. IN PUNICODE_STRING BaseClassName,
  1613. IN ULONG Number,
  1614. OUT PUNICODE_STRING DotClassName
  1615. )
  1616. /*++dvdf - code complete - compiles clean - not tested
  1617. Routine Description:
  1618. This routine creates a ClassName for a ParClass PDO of the form:
  1619. L"\Device\ParallelN.M" where L"\Device\ParallelN" is the
  1620. BaseClassName and M is the wide string representation of
  1621. the 'Number' parameter. The returned value is intended to be
  1622. used as the ClassName of a ParClass 1284.3 Daisy Chain device
  1623. or a ParClass End-Of-Chain PnP device. BaseClassName is not
  1624. modified.
  1625. Note: On success DotClassName->Buffer points to allocated pool.
  1626. The caller is responsible for freeing this allocation when
  1627. it is no longer required.
  1628. Note: The returned DotClassName->Buffer is UNICODE_NULL terminated.
  1629. Arguments:
  1630. BaseClassName - points to the ClassName of the PODO for the Raw
  1631. host port to which this device is connected
  1632. Number - supplies the 1284.3 daisy chain device ID [0..3] for
  1633. a 1284.3 daisy chain device or 4 for an
  1634. End-Of-Chain PnP device.
  1635. DotClassName - returns the ClassName to be used for the PDO on success,
  1636. (DotClassName->Buffer == NULL) indicates failure
  1637. Return Value:
  1638. None - caller determines success or failure by examining DotClassName->Buffer.
  1639. --*/
  1640. {
  1641. NTSTATUS status;
  1642. UNICODE_STRING digits;
  1643. UNICODE_STRING dot;
  1644. WCHAR digitsBuffer[10];
  1645. PAGED_CODE();
  1646. if( Number > DOT3_LEGACY_ZIP_ID ) {
  1647. // 0..3 are Daisy Chain devices, 4 is End-Of-Chain device, 5 is Legacy Zip
  1648. RtlInitUnicodeString(DotClassName, NULL);
  1649. return;
  1650. }
  1651. RtlInitUnicodeString(&dot, (PWSTR)L".");
  1652. digits.Length = 0;
  1653. digits.MaximumLength = sizeof(digitsBuffer);
  1654. digits.Buffer = digitsBuffer;
  1655. status = RtlIntegerToUnicodeString(Number, 10, &digits);
  1656. if ( !NT_SUCCESS(status) ) {
  1657. RtlInitUnicodeString(DotClassName, NULL);
  1658. return;
  1659. }
  1660. DotClassName->MaximumLength = (USHORT)(BaseClassName->Length + digits.Length + dot.Length + 2*sizeof(UNICODE_NULL));
  1661. DotClassName->Buffer = ExAllocatePool(PagedPool, DotClassName->MaximumLength);
  1662. if (!DotClassName->Buffer) {
  1663. RtlInitUnicodeString(DotClassName, NULL);
  1664. return;
  1665. }
  1666. RtlZeroMemory(DotClassName->Buffer, DotClassName->MaximumLength);
  1667. RtlAppendUnicodeStringToString(DotClassName, BaseClassName);
  1668. RtlAppendUnicodeStringToString(DotClassName, &dot);
  1669. RtlAppendUnicodeStringToString(DotClassName, &digits);
  1670. return;
  1671. }
  1672. VOID
  1673. ParAcquireListMutexAndKillDeviceObject(
  1674. IN PDEVICE_OBJECT Fdo,
  1675. IN PDEVICE_OBJECT DevObj
  1676. )
  1677. /*++dvdf - code complete - compiles clean - not tested
  1678. Routine Description:
  1679. This function provides a wrapper around ParKillDeviceObject() that
  1680. handles acquiring and releasing the Mutex that protects the list
  1681. of ParClass created PDOs and PODOs.
  1682. ParKillDeviceObject() requires that its caller hold the FDO ListMutex.
  1683. Arguments:
  1684. Fdo - points to the ParClass FDO (The Mutex resides in the FDO extension)
  1685. DevObj - points to the DeviceObject to be killed
  1686. Return Value:
  1687. None
  1688. --*/
  1689. {
  1690. PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
  1691. PAGED_CODE();
  1692. ExAcquireFastMutex(&fdoExt->DevObjListMutex);
  1693. ParKillDeviceObject(DevObj);
  1694. ExReleaseFastMutex(&fdoExt->DevObjListMutex);
  1695. }
  1696. VOID
  1697. ParAddDevObjToFdoList(
  1698. IN PDEVICE_OBJECT DevObj
  1699. )
  1700. /*++dvdf - code complete - compiles clean - not tested
  1701. Routine Description:
  1702. This function adds a PDO or PODO to the list of ParClass
  1703. created PODOs and PDOs. The DevObj is added to the front
  1704. of the list.
  1705. Arguments:
  1706. DevObj - points to the DeviceObject (PDO or PODO) to be added to the list
  1707. Return Value:
  1708. None - This function can not fail.
  1709. --*/
  1710. {
  1711. PDEVICE_EXTENSION devObjExt = DevObj->DeviceExtension;
  1712. PDEVICE_EXTENSION fdoExt = devObjExt->ParClassFdo->DeviceExtension;
  1713. PAGED_CODE();
  1714. ExAcquireFastMutex(&fdoExt->DevObjListMutex);
  1715. devObjExt->Next = fdoExt->ParClassPdo;
  1716. fdoExt->ParClassPdo = DevObj;
  1717. ExReleaseFastMutex(&fdoExt->DevObjListMutex);
  1718. }
  1719.