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.

1410 lines
47 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1998 - 1999
  6. //
  7. // File: devobj.c
  8. //
  9. //--------------------------------------------------------------------------
  10. // this file contains functions to create, initialize, manage, and destroy ParClass device objects
  11. #include "pch.h"
  12. extern WCHAR ParInt2Wchar[];
  13. VOID
  14. ParMakeClassNameFromPortLptName(
  15. IN PUNICODE_STRING PortSymbolicLinkName,
  16. OUT PUNICODE_STRING ClassName
  17. )
  18. /*
  19. Get LPTx name from ParPort device and use this name to construct the \Device\Parallely name.
  20. y = x-1 (i.e., LPT3 => \Device\Parallel2)
  21. */
  22. {
  23. NTSTATUS status;
  24. PDEVICE_OBJECT portDeviceObject;
  25. PFILE_OBJECT portDeviceFileObject;
  26. PWSTR portName;
  27. ULONG portNumber;
  28. LONG count;
  29. UNICODE_STRING str;
  30. // Get a pointer to the ParPort device
  31. status = IoGetDeviceObjectPointer(PortSymbolicLinkName, STANDARD_RIGHTS_ALL,
  32. &portDeviceFileObject, &portDeviceObject);
  33. if( !NT_SUCCESS(status) ) {
  34. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - unable to get handle to parport device\n"));
  35. return;
  36. }
  37. // Get LPTx portName from ParPort device
  38. portName = ParGetPortLptName( portDeviceObject );
  39. // Done with handle
  40. ObDereferenceObject( portDeviceFileObject );
  41. // Did we get a portName?
  42. if( 0 == portName ) {
  43. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - unable to get portName from parport device\n"));
  44. return;
  45. }
  46. // Verify that the portname looks like LPTx where x is a number
  47. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - portName = <%S>\n", portName));
  48. if( portName[0] != L'L' || portName[1] != L'P' || portName[2] != L'T' ) {
  49. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - name prefix doesn't look like LPT\n"));
  50. return;
  51. }
  52. // prefix is LPT, check for integer suffix with value > 0
  53. RtlInitUnicodeString( &str, (PWSTR)&portName[3] );
  54. status = RtlUnicodeStringToInteger( &str, 10, &portNumber );
  55. if( !NT_SUCCESS( status ) ) {
  56. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - name suffix doesn't look like an integer\n"));
  57. return;
  58. }
  59. if( portNumber == 0 ) {
  60. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - name suffix == 0 - FAIL - Invalid value\n"));
  61. return;
  62. }
  63. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - LPT name suffix= %d\n", portNumber));
  64. // Build \Device\Parallely name from LPTx name
  65. ParMakeClassNameFromNumber( portNumber-1, ClassName );
  66. ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - portName=<%S> className=<%wZ>\n",
  67. portName, ClassName));
  68. }
  69. // return TRUE if given a pointer to a ParClass PODO, FALSE otherwise
  70. BOOLEAN
  71. ParIsPodo(PDEVICE_OBJECT DevObj) {
  72. PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
  73. if( !devExt->IsPdo ) {
  74. // this is an FDO
  75. return FALSE;
  76. }
  77. // still here? - this is either a PODO or a PDO
  78. if( devExt->DeviceIdString[0] != 0 ) {
  79. // this device object has a device ID string - It's a PDO
  80. return FALSE;
  81. }
  82. // still here? - this is either a PODO, or a PDO marked "hardware gone" that
  83. // is simply waiting for PnP to send it a REMOVE.
  84. if( devExt->DeviceStateFlags & PAR_DEVICE_HARDWARE_GONE) {
  85. // this is a PDO marked "hardware gone"
  86. return FALSE;
  87. }
  88. // still here? - this is a PODO
  89. return TRUE;
  90. }
  91. #if USE_TEMP_FIX_FOR_MULTIPLE_INTERFACE_ARRIVAL
  92. //
  93. // Temp fix for PnP calling us multiple times for the same interface arrival
  94. //
  95. //
  96. // Return Value: Did we already process an interface arrival for this interface?
  97. //
  98. BOOLEAN
  99. ParIsDuplicateInterfaceArrival(
  100. IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStruct,
  101. IN PDEVICE_OBJECT Fdo
  102. )
  103. {
  104. PUNICODE_STRING portSymbolicLinkName = NotificationStruct->SymbolicLinkName;
  105. PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
  106. PDEVICE_OBJECT curDevObj;
  107. PDEVICE_EXTENSION curDevExt;
  108. PAGED_CODE();
  109. ParDump2(PARPNP1, ("Enter ParIsDuplicateInterfaceArrival()\n"));
  110. //
  111. // Find the ParClass PODOs and compare the PortSymbolicLinkName in the
  112. // device extension with the one in the interface arrival notification.
  113. //
  114. curDevObj = fdoExt->ParClassPdo;
  115. while( curDevObj ) {
  116. curDevExt = curDevObj->DeviceExtension;
  117. if( ParIsPodo(curDevObj) ) {
  118. ParDump2(PARPNP1, ("DevObj= %x IS PODO\n",curDevObj) );
  119. ParDump2(PARPNP1, ("Comparing port symbolic link names\n"));
  120. if( RtlEqualUnicodeString(portSymbolicLinkName,&curDevExt->PortSymbolicLinkName, FALSE) ) {
  121. ParDump2(PARPNP1, ("MATCH! - Second Arrival of this Interface\n"));
  122. return TRUE;
  123. } else {
  124. ParDump2(PARPNP1, ("NO match on port symbolic link name for interface arrival - keep searching\n"));
  125. }
  126. } else {
  127. ParDump2(PARPNP1, ("DevObj= %x NOT a PODO\n",curDevObj) );
  128. }
  129. curDevObj = curDevExt->Next;
  130. }
  131. return FALSE;
  132. }
  133. #endif
  134. NTSTATUS
  135. ParPnpNotifyInterfaceChange(
  136. IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStruct,
  137. IN PDEVICE_OBJECT Fdo
  138. )
  139. /*++dvdf
  140. Routine Description:
  141. This routine is the PnP "interface change notification" callback routine.
  142. This gets called on a ParPort triggered device interface arrival or removal.
  143. - Interface arrival corresponds to a ParPort device being STARTed
  144. - Interface removal corresponds to a ParPort device being REMOVEd
  145. On arrival:
  146. - Create the LPTx PODO (PlainOldDeviceObject) for legacy/raw port access.
  147. - Query for an EOC (End-Of-Chain) PnP Device, create PDO if device found.
  148. - Enumerate all 1284.3 DC (Daisy Chain) devices attached to the port.
  149. This callback is a NO-OP for interface removal because all ParClass created
  150. P[O]DOs register for PnP EventCategoryTargetDeviceChange callbacks and
  151. use that callback to clean up when their associated ParPort device goes
  152. away.
  153. Arguments:
  154. NotificationStruct - Structure defining the change.
  155. Fdo - pointer to ParClass FDO
  156. (supplied as the "context" when we
  157. registered for this callback)
  158. Return Value:
  159. STATUS_SUCCESS - always, even if something goes wrong
  160. --*/
  161. {
  162. PUNICODE_STRING portSymbolicLinkName = NotificationStruct->SymbolicLinkName;
  163. PDEVICE_OBJECT legacyPodo;
  164. PDEVICE_EXTENSION legacyExt;
  165. // PDEVICE_OBJECT endOfChainPdo;
  166. PDEVICE_OBJECT portDeviceObject;
  167. PFILE_OBJECT portDeviceFileObject;
  168. NTSTATUS status;
  169. BOOLEAN foundNewDevice = FALSE;
  170. // UCHAR dot3DeviceCount;
  171. PAGED_CODE();
  172. //
  173. // Verify that interface class is a ParPort device interface.
  174. //
  175. // Any other InterfaceClassGuid is an error, but let it go since
  176. // it is not fatal to the machine.
  177. //
  178. if( !IsEqualGUID( (LPGUID)&(NotificationStruct->InterfaceClassGuid),
  179. (LPGUID)&GUID_PARALLEL_DEVICE) ) {
  180. ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - Bad InterfaceClassGuid\n") );
  181. return STATUS_SUCCESS;
  182. }
  183. //
  184. // This callback is a NO-OP for interface removal.
  185. //
  186. // All ParClass DO's that depend on this interface register for target
  187. // device change PnP notification on the ParPort device associated
  188. // with this interface.
  189. //
  190. // Thus, all such ParClass DO's that depend on this interface will
  191. // have already received target device change notification callbacks
  192. // that the ParPort device associated with this interface is being
  193. // removed prior to the arrival of this interface removal
  194. // notification callback.
  195. //
  196. if(!IsEqualGUID( (LPGUID)&(NotificationStruct->Event),
  197. (LPGUID)&GUID_DEVICE_INTERFACE_ARRIVAL )) {
  198. ParDump2(PARPNP1, ("Interface Removal Notification\n") );
  199. return STATUS_SUCCESS;
  200. }
  201. //
  202. // A ParPort device has STARTed and we are the bus driver for the port.
  203. // Continue...
  204. //
  205. #if USE_TEMP_FIX_FOR_MULTIPLE_INTERFACE_ARRIVAL
  206. //
  207. // Temp fix for PnP calling us multiple times for the same interface arrival
  208. //
  209. if( ParIsDuplicateInterfaceArrival(NotificationStruct, Fdo) ) {
  210. ParDump2(PARERRORS, ("Duplicate Interface Arrival Notification - Returning/Ignoring\n") );
  211. return STATUS_SUCCESS;
  212. }
  213. #endif
  214. //
  215. // Get a pointer to and create a FILE against the ParPort device
  216. //
  217. status = IoGetDeviceObjectPointer(portSymbolicLinkName, STANDARD_RIGHTS_ALL,
  218. &portDeviceFileObject, &portDeviceObject);
  219. if( !NT_SUCCESS(status) ) {
  220. ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - unable to get device object port to ParPort device\n") );
  221. return STATUS_SUCCESS;
  222. }
  223. //
  224. // Acquire the ParPort device
  225. //
  226. status = ParAcquirePort(portDeviceObject, NULL);
  227. if( !NT_SUCCESS(status) ) {
  228. ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - unable to acquire port\n") );
  229. ObDereferenceObject(portDeviceFileObject);
  230. return STATUS_SUCCESS;
  231. }
  232. //
  233. // Create the Legacy LPTx PODO (PlainOldDeviceObject) for raw port access.
  234. //
  235. legacyPodo = ParCreateLegacyPodo(Fdo, portSymbolicLinkName);
  236. if( !legacyPodo ) {
  237. // If we can't create the legacyPodo, then nothing following will
  238. // succeed, so bail out.
  239. ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - unable to create legacyPodo\n") );
  240. ParReleasePort(portDeviceObject);
  241. ObDereferenceObject(portDeviceFileObject);
  242. return STATUS_SUCCESS;
  243. }
  244. //
  245. // SUCCESS - Legacy PODO created
  246. //
  247. legacyExt = legacyPodo->DeviceExtension;
  248. ParDump2(PARPNP1, ("devobj::ParPnpNotifyInterfaceChange - CREATED legacyPODO - <%wZ> <%wZ>\n",
  249. &legacyExt->ClassName, &legacyExt->SymbolicLinkName) );
  250. //
  251. // Legacy PODO - add to list of ParClass created device objects
  252. //
  253. ParAddDevObjToFdoList(legacyPodo);
  254. //
  255. // release port and close our FILE to Parport device (our PODO and PDOs
  256. // have their own FILEs open against parport)
  257. //
  258. ParReleasePort(portDeviceObject);
  259. ObDereferenceObject(portDeviceFileObject);
  260. //
  261. // Tell PnP that we might have some new children
  262. //
  263. {
  264. PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
  265. ParDump2(PARPNP1, ("devobj::ParPnpNotifyInterfaceChange - calling IoInvalidateDeviceRelations(,BusRelations)\n") );
  266. IoInvalidateDeviceRelations(fdoExt->PhysicalDeviceObject, BusRelations);
  267. }
  268. return STATUS_SUCCESS;
  269. }
  270. PDEVICE_OBJECT
  271. ParCreateLegacyPodo(
  272. IN PDEVICE_OBJECT Fdo,
  273. IN PUNICODE_STRING PortSymbolicLinkName
  274. )
  275. /*++dvdf
  276. Routine Description:
  277. This routine creates the LPTx PODO (PlainOldDeviceObject) that is
  278. used for legacy/raw port access.
  279. - create a classname of the form "\Device\ParallelN"
  280. - create device object
  281. - initialize device object and extension
  282. - create symbolic link
  283. - register for PnP TargetDeviceChange notification
  284. Arguments:
  285. Fdo - pointer to ParClass FDO
  286. PortSymbolicLinkName - symbolic link name of the ParPort device
  287. Return Value:
  288. PDEVICE_OBJECT - on success
  289. NULL - otherwise
  290. --*/
  291. {
  292. NTSTATUS status;
  293. UNICODE_STRING className = {0,0,0};
  294. PDEVICE_OBJECT legacyPodo;
  295. PDEVICE_EXTENSION legacyExt;
  296. PDRIVER_OBJECT driverObject = Fdo->DriverObject;
  297. #define PAR_CLASSNAME_OFFSET 8
  298. PAGED_CODE();
  299. //
  300. // Legacy PODO - try to build a \Device\Parallely classname based on the LPTx name
  301. // retrieved from the ParPort device
  302. //
  303. ParMakeClassNameFromPortLptName(PortSymbolicLinkName, &className);
  304. if( !className.Buffer ) {
  305. //
  306. // We failed to construct a ClassName from the Port's
  307. // LPTx name - just make up a name
  308. //
  309. // Use an offset so that the name we make up doesn't collide
  310. // with other ports
  311. //
  312. ParMakeClassNameFromNumber( PAR_CLASSNAME_OFFSET + g_NumPorts++, &className );
  313. if( !className.Buffer ) {
  314. // unable to create class name, bail out
  315. return NULL;
  316. }
  317. }
  318. //
  319. // Legacy PODO - create device object
  320. //
  321. status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &className,
  322. FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &legacyPodo);
  323. if( !NT_SUCCESS( status ) ) {
  324. //
  325. // We failed to create a device, if failure was due to a name collision, then
  326. // make up a new classname and try again
  327. //
  328. if( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_NAME_EXISTS ) {
  329. // name collision - make up a new classname and try again
  330. RtlFreeUnicodeString( &className );
  331. ParMakeClassNameFromNumber( PAR_CLASSNAME_OFFSET + g_NumPorts++, &className );
  332. if( !className.Buffer ) {
  333. // unable to create class name, bail out
  334. return NULL;
  335. }
  336. status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &className,
  337. FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &legacyPodo);
  338. }
  339. }
  340. if( !NT_SUCCESS( status ) ) {
  341. // unable to create device object, bail out
  342. RtlFreeUnicodeString(&className);
  343. ParLogError(driverObject, NULL, PhysicalZero, PhysicalZero,
  344. 0, 0, 0, 9, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
  345. return NULL;
  346. }
  347. legacyExt = legacyPodo->DeviceExtension;
  348. //
  349. // Legacy PODO - initialize device object and extension
  350. //
  351. ParInitCommonDOPre(legacyPodo, Fdo, &className);
  352. status = ParInitLegacyPodo(legacyPodo, PortSymbolicLinkName);
  353. if( !NT_SUCCESS( status ) ) {
  354. // initialization failed, clean up and bail out
  355. ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
  356. return NULL;
  357. }
  358. //
  359. // Note that we are a PODO in our extension
  360. //
  361. legacyExt->DeviceType = PAR_DEVTYPE_PODO;
  362. ParInitCommonDOPost(legacyPodo);
  363. //
  364. // Legacy PODO - create symbolic link
  365. //
  366. if( legacyExt->SymbolicLinkName.Buffer ) {
  367. status = IoCreateUnprotectedSymbolicLink(&legacyExt->SymbolicLinkName,
  368. &legacyExt->ClassName);
  369. if ( NT_SUCCESS(status) ) {
  370. // note this in our extension for later cleanup
  371. legacyExt->CreatedSymbolicLink = TRUE;
  372. // Write symbolic link map info to the registry.
  373. status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
  374. (PWSTR)L"PARALLEL PORTS",
  375. legacyExt->ClassName.Buffer,
  376. REG_SZ,
  377. legacyExt->SymbolicLinkName.Buffer,
  378. legacyExt->SymbolicLinkName.Length +
  379. sizeof(WCHAR));
  380. if (!NT_SUCCESS(status)) {
  381. // unable to write map info to registry - continue anyway
  382. ParLogError(legacyPodo->DriverObject, legacyPodo,
  383. legacyExt->OriginalController, PhysicalZero,
  384. 0, 0, 0, 6, status, PAR_NO_DEVICE_MAP_CREATED);
  385. }
  386. } else {
  387. // unable to create the symbolic link.
  388. legacyExt->CreatedSymbolicLink = FALSE;
  389. RtlFreeUnicodeString(&legacyExt->SymbolicLinkName);
  390. ParLogError(legacyPodo->DriverObject, legacyPodo,
  391. legacyExt->OriginalController, PhysicalZero,
  392. 0, 0, 0, 5, status, PAR_NO_SYMLINK_CREATED);
  393. }
  394. } else {
  395. // extension does not contain a symbolic link name for us to use
  396. legacyExt->CreatedSymbolicLink = FALSE;
  397. }
  398. if( FALSE == legacyExt->CreatedSymbolicLink ) {
  399. // Couldn't create a symbolic Link
  400. // clean up and bail out
  401. ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
  402. return NULL;
  403. }
  404. //
  405. // Legacy PODO - register for PnP TargetDeviceChange notification
  406. //
  407. status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
  408. 0,
  409. (PVOID)legacyExt->PortDeviceFileObject,
  410. driverObject,
  411. (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) ParPnpNotifyTargetDeviceChange,
  412. (PVOID)legacyPodo,
  413. &legacyExt->NotificationHandle);
  414. if( !NT_SUCCESS(status) ) {
  415. // PnP registration for TargetDeviceChange notification failed,
  416. // clean up and bail out
  417. ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
  418. return NULL;
  419. }
  420. //
  421. // Register for WMI
  422. //
  423. status = ParWmiPdoInitWmi( legacyPodo );
  424. if( !NT_SUCCESS( status ) ) {
  425. // WMI registration failed, clean up and bail out
  426. ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
  427. return NULL;
  428. } else {
  429. //
  430. // Note in our extension that we have registered for WMI
  431. // so we can clean up later
  432. //
  433. legacyExt->PodoRegForWMI = TRUE;
  434. }
  435. return legacyPodo;
  436. }
  437. VOID
  438. ParInitCommonDOPre(
  439. IN PDEVICE_OBJECT DevObj,
  440. IN PDEVICE_OBJECT Fdo,
  441. IN PUNICODE_STRING ClassName
  442. )
  443. /*++dvdf - code complete - compiles clean - not tested
  444. Routine Description:
  445. This routine contains common initialization code for ParClass
  446. created PDOs and PODOs that should be called before (Pre) the
  447. device object type specific (PDO/PODO) intialization function
  448. is called.
  449. Arguments:
  450. DevObj - points to the DeviceObject to be initialized
  451. Fdo - points to the ParClass FDO
  452. ClassName - points to the ClassName for the PDO/PODO
  453. Return Value:
  454. None - This function can not fail.
  455. --*/
  456. {
  457. PDEVICE_EXTENSION extension;
  458. PAGED_CODE();
  459. // - we use buffered IO
  460. // - force power dispatch at PASSIVE_LEVEL IRQL so we can page driver
  461. // DevObj->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE);
  462. DevObj->Flags |= DO_BUFFERED_IO; // RMT - should also set POWER_PAGABLE
  463. // initialize extension to all zeros
  464. extension = DevObj->DeviceExtension;
  465. RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
  466. // used by debugger extension
  467. extension->ExtensionSignature = PARCLASS_EXTENSION_SIGNATURE;
  468. extension->ExtensionSignatureEnd = PARCLASS_EXTENSION_SIGNATURE;
  469. // initialize synchronization and list mechanisms
  470. ExInitializeFastMutex(&extension->OpenCloseMutex);
  471. InitializeListHead(&extension->WorkQueue);
  472. KeInitializeSemaphore(&extension->RequestSemaphore, 0, MAXLONG);
  473. KeInitializeEvent(&extension->PauseEvent, NotificationEvent, TRUE);
  474. // general info
  475. extension->ClassName = *ClassName; // copy the struct
  476. extension->DeviceObject = DevObj;
  477. extension->EndOfChain = TRUE; // override later if this is a
  478. extension->Ieee1284_3DeviceId = DOT3_END_OF_CHAIN_ID; // 1284.3 Daisy Chain device
  479. extension->IsPdo = TRUE; // really means !FDO
  480. extension->ParClassFdo = Fdo;
  481. extension->BusyDelay = 0;
  482. extension->BusyDelayDetermined = FALSE;
  483. // timing constants
  484. extension->TimerStart = PAR_WRITE_TIMEOUT_VALUE;
  485. extension->IdleTimeout.QuadPart -= 250*10*1000; // 250 ms
  486. extension->AbsoluteOneSecond.QuadPart = 10*1000*1000;
  487. extension->OneSecond.QuadPart = -(extension->AbsoluteOneSecond.QuadPart);
  488. // init IEEE 1284 protocol settings
  489. ParInitializeExtension1284Info( extension );
  490. }
  491. NTSTATUS
  492. ParInitLegacyPodo(
  493. IN PDEVICE_OBJECT LegacyPodo,
  494. IN PUNICODE_STRING PortSymbolicLinkName
  495. )
  496. /*++
  497. Routine Description:
  498. This function performs ParClass DeviceObject and DeviceExtension
  499. initialization specific to ParClass Legacy PODOs (Plain Old Device
  500. Objects). A Legacy PODO represents the "raw" parallel port and is
  501. used by legacy drivers to communicate with parallel port connected
  502. devices.
  503. Precondition:
  504. - ParInitCommonPre(...) must be called with this DeviceObject before
  505. this function is called.
  506. Postcondition:
  507. - ParInitCommonPost(...) must be called with this DeviceObject after
  508. this function is called.
  509. On error, this routine defers cleaning up the LegacyPodo DeviceObject
  510. and any pool allocations to the caller.
  511. Arguments:
  512. LegacyPodo - pointer to the legacy device object to initialize
  513. PortSymbolicLinkName - symbolic link name of ParPort device
  514. Return Value:
  515. STATUS_SUCCESS - on success
  516. STATUS_UNSUCCESSFUL - otherwise
  517. --*/
  518. {
  519. NTSTATUS status = STATUS_SUCCESS;
  520. PDEVICE_EXTENSION legacyExt = LegacyPodo->DeviceExtension;
  521. PWSTR buffer = NULL;
  522. PFILE_OBJECT portDeviceFileObject = NULL;
  523. PDEVICE_OBJECT portDeviceObject = NULL;
  524. PAGED_CODE();
  525. //
  526. // Get a pointer to and create a FILE against the ParPort device
  527. //
  528. // - We need a pointer to the ParPort DeviceObject so that we can send
  529. // it IRPs.
  530. //
  531. // - We need a FILE against the DeviceObject to keep the ParPort
  532. // DeviceObject from "going away" while we are using it.
  533. //
  534. // - Having an open FILE against the ParPort DeviceObject will also prevent
  535. // PnP from doing a resource rebalance on the ParPort DeviceObject and
  536. // changing its resources while we are holding pointers to the ParPort
  537. // device's registers. This works because a ParPort DeviceObject fails
  538. // PnP QUERY_STOP IRPs if anyone has an open FILE against it.
  539. //
  540. status = IoGetDeviceObjectPointer(PortSymbolicLinkName,
  541. STANDARD_RIGHTS_ALL,
  542. &portDeviceFileObject,
  543. &portDeviceObject);
  544. if( !NT_SUCCESS(status) ) {
  545. return STATUS_UNSUCCESSFUL;
  546. }
  547. //
  548. // success - save pointer to ParPort DeviceObject and a copy of the
  549. // REFERENCED pointer to the FILE created against the ParPort
  550. // DeviceObject in our extension
  551. //
  552. legacyExt->PortDeviceObject = portDeviceObject;
  553. legacyExt->PortDeviceFileObject = portDeviceFileObject;
  554. //
  555. // Save a copy of the ParPort SymbolicLinkName in our device extension.
  556. //
  557. buffer = ParCreateWideStringFromUnicodeString(PortSymbolicLinkName);
  558. if( !buffer ) {
  559. // unable to copy PortSymbolicLinkName, bail out
  560. return STATUS_UNSUCCESSFUL;
  561. }
  562. // copy ParPort SymbolicLinkName to our device extension
  563. RtlInitUnicodeString(&legacyExt->PortSymbolicLinkName, buffer);
  564. //
  565. // make sure that IRPs sent to us have enough stack locations so that we
  566. // can forward them to ParPort if needed
  567. //
  568. legacyExt->DeviceObject->StackSize = (CHAR)( legacyExt->PortDeviceObject->StackSize + 1 );
  569. //
  570. // Obtain PARALLEL_PORT_INFORMATION and PARALLEL_PNP_INFORMATION from
  571. // the ParPort device and save it in our device extension
  572. //
  573. status = ParGetPortInfoFromPortDevice(legacyExt);
  574. if (!NT_SUCCESS(status)) {
  575. ParLogError(LegacyPodo->DriverObject, LegacyPodo, PhysicalZero, PhysicalZero,
  576. 0, 0, 0, 4, status, PAR_CANT_FIND_PORT_DRIVER);
  577. return STATUS_UNSUCCESSFUL;
  578. }
  579. if (legacyExt->OriginalController.HighPart == 0 &&
  580. legacyExt->OriginalController.LowPart == (ULONG_PTR) legacyExt->Controller) {
  581. legacyExt->UsePIWriteLoop = FALSE;
  582. } else {
  583. legacyExt->UsePIWriteLoop = TRUE;
  584. }
  585. return STATUS_SUCCESS;
  586. }
  587. VOID
  588. ParInitCommonDOPost(
  589. IN PDEVICE_OBJECT DevObj
  590. )
  591. /*++dvdf - code complete - compiles clean - not tested
  592. Routine Description:
  593. This routine contains common initialization code for ParClass
  594. created PDOs and PODOs that should be called after (Post) the
  595. device object type specific (PDO/PODO) intialization function
  596. is called.
  597. Arguments:
  598. DevObj - points to the DeviceObject to be initialized
  599. Return Value:
  600. None - This function can not fail.
  601. --*/
  602. {
  603. PAGED_CODE();
  604. // Check the registry for parameter overrides
  605. ParCheckParameters(DevObj->DeviceExtension);
  606. // Tell the IO system that we are ready to receive IRPs
  607. DevObj->Flags &= ~DO_DEVICE_INITIALIZING;
  608. }
  609. PDEVICE_OBJECT
  610. ParDetectCreateEndOfChainPdo(
  611. IN PDEVICE_OBJECT LegacyPodo
  612. )
  613. /*++
  614. Routine Description:
  615. Detect if an EndOfChain device is connected. If so, create a PDO for the
  616. device and add the PDO to the list of ParClass children.
  617. Arguments:
  618. LegacyPodo - pointer to the legacy PODO for the port.
  619. Return Value:
  620. Pointer to the new PDO if device found
  621. NULL otherwise
  622. --*/
  623. {
  624. PDEVICE_OBJECT newPdo = ParDetectCreatePdo( LegacyPodo, DOT3_END_OF_CHAIN_ID, FALSE );
  625. if( newPdo ) {
  626. ParAddDevObjToFdoList( newPdo );
  627. }
  628. return newPdo;
  629. }
  630. PDEVICE_OBJECT
  631. ParDetectCreatePdo(
  632. IN PDEVICE_OBJECT LegacyPodo,
  633. IN UCHAR Dot3Id,
  634. IN BOOLEAN bStlDot3Id
  635. )
  636. /*++
  637. Routine Description:
  638. Detect if there is a 1284 device attached.
  639. If a device is detected then create a PDO to represent the device.
  640. Preconditions:
  641. Caller has acquired the ParPort device
  642. Caller has SELECTed the device
  643. Postconditions:
  644. ParPort device is still acquired
  645. Device is still SELECTed
  646. Arguments:
  647. LegacyPodo - points to the Legacy PODO for the port
  648. Dot3Id - 1284.3 daisy chain id
  649. 0..3 is daisy chain device
  650. 4 is end of chain device
  651. Return Value:
  652. PDEVICE_OBJECT - on success, points to the PDO we create
  653. NULL - otherwise
  654. --*/
  655. {
  656. PDEVICE_EXTENSION legacyExt = LegacyPodo->DeviceExtension;
  657. PDRIVER_OBJECT driverObject = LegacyPodo->DriverObject;
  658. PDEVICE_OBJECT fdo = legacyExt->ParClassFdo;
  659. UNICODE_STRING className = {0,0,NULL};
  660. NTSTATUS status;
  661. PCHAR deviceIdString = NULL;
  662. ULONG deviceIdLength;
  663. PDEVICE_OBJECT newDevObj = NULL;
  664. PDEVICE_EXTENSION newDevExt;
  665. ULONG idTry = 1;
  666. ULONG maxIdTries = 3;
  667. BOOLEAN useModifiedClassName = FALSE;
  668. UNICODE_STRING modifiedClassName;
  669. UNICODE_STRING suffix;
  670. UNICODE_STRING dash;
  671. WCHAR suffixBuffer[10];
  672. ULONG number = 0;
  673. ULONG maxNumber = 256;
  674. ULONG newLength;
  675. PAGED_CODE();
  676. //
  677. // Query for PnP device
  678. //
  679. while( (NULL == deviceIdString) && (idTry <= maxIdTries) ) {
  680. deviceIdString = Par3QueryDeviceId(legacyExt, NULL, 0, &deviceIdLength, FALSE, bStlDot3Id);
  681. if( NULL == deviceIdString ) {
  682. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - no 1284 ID on try %d\n", idTry) );
  683. KeStallExecutionProcessor(1);
  684. ++idTry;
  685. } else {
  686. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - devIdString=<%s> on try %d\n", deviceIdString, idTry) );
  687. }
  688. }
  689. if( !deviceIdString ) {
  690. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - no 1284 ID, bail out\n") );
  691. return NULL;
  692. }
  693. //
  694. // Found PnP Device, create a PDO to represent the device
  695. // - create classname
  696. // - create device object
  697. // - initialize device object and extension
  698. // - create symbolic link
  699. // - register for PnP TargetDeviceChange notification
  700. //
  701. //
  702. // Create a class name of the form \Device\ParallelN,
  703. //
  704. ParMakeDotClassNameFromBaseClassName(&legacyExt->ClassName, Dot3Id, &className);
  705. if( !className.Buffer ) {
  706. // unable to construct ClassName
  707. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - unable to construct ClassName for device\n") );
  708. ExFreePool(deviceIdString);
  709. return NULL;
  710. }
  711. //
  712. // create device object
  713. //
  714. status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &className,
  715. FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &newDevObj);
  716. ///
  717. if( status == STATUS_OBJECT_NAME_COLLISION ) {
  718. //
  719. // old name is still in use, appending a suffix and try again
  720. //
  721. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ParCreateDevice FAILED due to name Collision on <%wZ> - retry\n", &className));
  722. useModifiedClassName = TRUE;
  723. suffix.Length = 0;
  724. suffix.MaximumLength = sizeof(suffixBuffer);
  725. suffix.Buffer = suffixBuffer;
  726. RtlInitUnicodeString( &dash, (PWSTR)L"-" );
  727. newLength = className.MaximumLength + 5*sizeof(WCHAR); // L"-XXX" suffix
  728. modifiedClassName.Length = 0;
  729. modifiedClassName.MaximumLength = (USHORT)newLength;
  730. modifiedClassName.Buffer = ExAllocatePool(PagedPool, newLength);
  731. if( NULL == modifiedClassName.Buffer ) {
  732. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ParCreateDevice FAILED - no PagedPool avail\n"));
  733. ExFreePool(deviceIdString);
  734. RtlFreeUnicodeString( &className );
  735. return NULL;
  736. }
  737. while( ( status == STATUS_OBJECT_NAME_COLLISION ) && ( number <= maxNumber ) ) {
  738. status = RtlIntegerToUnicodeString(number, 10, &suffix);
  739. if ( !NT_SUCCESS(status) ) {
  740. ExFreePool(deviceIdString);
  741. RtlFreeUnicodeString( &className );
  742. RtlFreeUnicodeString( &modifiedClassName );
  743. return NULL;
  744. }
  745. RtlCopyUnicodeString( &modifiedClassName, &className );
  746. RtlAppendUnicodeStringToString( &modifiedClassName, &dash );
  747. RtlAppendUnicodeStringToString( &modifiedClassName, &suffix );
  748. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - trying ParCreateDevice with className <%wZ>\n", &modifiedClassName));
  749. status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &modifiedClassName,
  750. FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &newDevObj);
  751. if( NT_SUCCESS( status ) ) {
  752. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ParCreateDevice returned SUCCESS with className <%wZ>\n", &modifiedClassName));
  753. } else {
  754. ++number;
  755. }
  756. }
  757. }
  758. ///
  759. if( useModifiedClassName ) {
  760. // copy modifiedClassName to className
  761. RtlFreeUnicodeString( &className );
  762. className = modifiedClassName;
  763. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - copy useModifiedClassName to className - className=<%wZ>\n", &className));
  764. }
  765. if( !NT_SUCCESS(status) ) {
  766. // unable to create device object, bail out
  767. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - unable to create device object "
  768. "className=<%wZ>, bail out - status=%x\n", &className, status) );
  769. ExFreePool(deviceIdString);
  770. RtlFreeUnicodeString(&className);
  771. ParLogError(fdo->DriverObject, NULL, PhysicalZero, PhysicalZero,
  772. 0, 0, 0, 9, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
  773. return NULL;
  774. } else {
  775. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - device created <%wZ>\n", &className));
  776. }
  777. //
  778. // device object created
  779. //
  780. newDevExt = newDevObj->DeviceExtension;
  781. //
  782. // initialize device object and extension
  783. //
  784. ParInitCommonDOPre(newDevObj, fdo, &className);
  785. status = ParInitPdo(newDevObj, (PUCHAR)deviceIdString, deviceIdLength, LegacyPodo, Dot3Id);
  786. if( !NT_SUCCESS( status ) ) {
  787. // initialization failed, clean up and bail out
  788. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - call to ParInitPdo failed, bail out\n") );
  789. ParAcquireListMutexAndKillDeviceObject(fdo, newDevObj);
  790. return NULL;
  791. }
  792. ParInitCommonDOPost(newDevObj);
  793. //
  794. // create symbolic link
  795. //
  796. if( newDevExt->SymbolicLinkName.Buffer ) {
  797. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ready to create symlink - SymbolicLinkName <%wZ>, ClassName <%wZ>\n",
  798. &newDevExt->SymbolicLinkName, &newDevExt->ClassName) );
  799. ParDump2(PARPNP1, (" - Length=%hd, MaximumLength=%hd\n", newDevExt->ClassName.Length, newDevExt->ClassName.MaximumLength) );
  800. // doug
  801. ASSERT(newDevExt->ClassName.Length < 100);
  802. PAGED_CODE();
  803. status = IoCreateUnprotectedSymbolicLink(&newDevExt->SymbolicLinkName, &newDevExt->ClassName);
  804. if ( NT_SUCCESS(status) ) {
  805. // note this in our extension for later cleanup
  806. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - SymbolicLinkName -> ClassName = <%wZ> -> <%wZ>\n",
  807. &newDevExt->SymbolicLinkName, &newDevExt->ClassName) );
  808. newDevExt->CreatedSymbolicLink = TRUE;
  809. // Write symbolic link map info to the registry.
  810. status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
  811. (PWSTR)L"PARALLEL PORTS",
  812. newDevExt->ClassName.Buffer,
  813. REG_SZ,
  814. newDevExt->SymbolicLinkName.Buffer,
  815. newDevExt->SymbolicLinkName.Length +
  816. sizeof(WCHAR));
  817. if (!NT_SUCCESS(status)) {
  818. // unable to write map info to registry - continue anyway
  819. ParLogError(newDevObj->DriverObject, newDevObj,
  820. newDevExt->OriginalController, PhysicalZero,
  821. 0, 0, 0, 6, status, PAR_NO_DEVICE_MAP_CREATED);
  822. }
  823. } else {
  824. // unable to create the symbolic link.
  825. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - unable to create SymbolicLink - status = %x\n",status));
  826. newDevExt->CreatedSymbolicLink = FALSE;
  827. RtlFreeUnicodeString(&newDevExt->SymbolicLinkName);
  828. ParLogError(newDevObj->DriverObject, newDevObj,
  829. newDevExt->OriginalController, PhysicalZero,
  830. 0, 0, 0, 5, status, PAR_NO_SYMLINK_CREATED);
  831. }
  832. } else {
  833. // extension does not contain a symbolic link name for us to use
  834. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - extension does not contain a symbolic link for us to use\n"));
  835. newDevExt->CreatedSymbolicLink = FALSE;
  836. }
  837. // End-Of-Chain PDO - register for PnP TargetDeviceChange notification
  838. status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
  839. 0,
  840. (PVOID)newDevExt->PortDeviceFileObject,
  841. driverObject,
  842. (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) ParPnpNotifyTargetDeviceChange,
  843. (PVOID)newDevObj,
  844. &newDevExt->NotificationHandle);
  845. if( !NT_SUCCESS(status) ) {
  846. // PnP registration for TargetDeviceChange notification failed,
  847. // clean up and bail out
  848. ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - PnP registration failed, killing PDO\n") );
  849. ParAcquireListMutexAndKillDeviceObject(fdo, newDevObj);
  850. return NULL;
  851. }
  852. return newDevObj;
  853. }
  854. NTSTATUS
  855. ParInitPdo(
  856. IN PDEVICE_OBJECT NewPdo,
  857. IN PUCHAR DeviceIdString,
  858. IN ULONG DeviceIdLength,
  859. IN PDEVICE_OBJECT LegacyPodo,
  860. IN UCHAR Dot3Id
  861. )
  862. {
  863. static WCHAR ParInt2Wchar[] = { L'0', L'1', L'2', L'3', L'4', L'5' };
  864. NTSTATUS status;
  865. PDEVICE_EXTENSION legacyExt = LegacyPodo->DeviceExtension;
  866. PDEVICE_EXTENSION newExt = NewPdo->DeviceExtension;
  867. //
  868. // start with identical initialization as done for Legacy PODO
  869. //
  870. status = ParInitLegacyPodo(NewPdo, &legacyExt->PortSymbolicLinkName);
  871. if( !NT_SUCCESS(status) ) {
  872. return status;
  873. }
  874. //
  875. // Fixup extension if we are a daisy chain device or legacy Zip
  876. //
  877. newExt->Ieee1284Flags = legacyExt->Ieee1284Flags;
  878. if( Dot3Id != DOT3_END_OF_CHAIN_ID ) {
  879. newExt->EndOfChain = FALSE;
  880. newExt->Ieee1284_3DeviceId = Dot3Id;
  881. // NewPdo->Flags &= ~DO_POWER_PAGABLE; // RMT - temp clear bit until ppa, disk, partmgr set it
  882. }
  883. //
  884. // Note that we are a PDO in our extension
  885. //
  886. newExt->DeviceType = PAR_DEVTYPE_PDO;
  887. //
  888. // use PODO's SymbolicLinkName + a ".N" suffix as the PDO's SymbolicLinkName
  889. //
  890. {
  891. UNICODE_STRING newSymLinkName;
  892. USHORT index;
  893. USHORT length = (USHORT)( newExt->SymbolicLinkName.Length + ( 3 * sizeof(WCHAR) ) );
  894. ParDump2(PARPNP1, ("devobj::ParInitPdo - old SymLinkName=%wZ\n", &newExt->SymbolicLinkName) );
  895. RtlInitUnicodeString(&newSymLinkName, NULL);
  896. newSymLinkName.Buffer = ExAllocatePool(PagedPool, length);
  897. if( !newSymLinkName.Buffer ) {
  898. return STATUS_UNSUCCESSFUL;
  899. }
  900. newSymLinkName.Length=0;
  901. newSymLinkName.MaximumLength=length;
  902. RtlCopyUnicodeString(&newSymLinkName, &newExt->SymbolicLinkName);
  903. index = (USHORT) ( (newExt->SymbolicLinkName.Length)/sizeof(WCHAR) );
  904. newSymLinkName.Buffer[index+0] = L'.';
  905. newSymLinkName.Buffer[index+1] = ParInt2Wchar[Dot3Id];
  906. newSymLinkName.Buffer[index+2] = L'\0';
  907. newSymLinkName.Length += (2 * sizeof(WCHAR));
  908. RtlFreeUnicodeString(&newExt->SymbolicLinkName);
  909. newExt->SymbolicLinkName = newSymLinkName;
  910. ParDump2(PARPNP1, ("devobj::ParInitPdo - new SymLinkName=%wZ\n", &newExt->SymbolicLinkName) );
  911. ParDump2(PARPNP1, (" - Length=%hd, MaximumLength=%hd\n", newExt->SymbolicLinkName.Length, newExt->SymbolicLinkName.MaximumLength) );
  912. }
  913. // initialize PnP fields of device extension
  914. {
  915. UCHAR RawString[128];
  916. UCHAR DescriptionString[128];
  917. PUCHAR deviceIdString;
  918. deviceIdString = ExAllocatePool(PagedPool, DeviceIdLength + sizeof(UCHAR) );
  919. if( deviceIdString ) {
  920. RtlCopyMemory(deviceIdString, DeviceIdString, DeviceIdLength);
  921. *(deviceIdString+DeviceIdLength) = 0; // NULL terminate
  922. }
  923. RtlZeroMemory( RawString, sizeof(RawString) );
  924. RtlZeroMemory( DescriptionString, sizeof(DescriptionString) );
  925. status = ParPnpGetId(DeviceIdString, BusQueryDeviceID, RawString, DescriptionString);
  926. if (NT_SUCCESS(status)) {
  927. RtlCopyMemory(newExt->DeviceIdString, RawString, strlen((const PCHAR)RawString));
  928. RtlCopyMemory(newExt->DeviceDescription, DescriptionString, strlen((const PCHAR)DescriptionString));
  929. if( deviceIdString ) {
  930. ParDetectDot3DataLink(newExt, deviceIdString);
  931. }
  932. }
  933. if( deviceIdString ) {
  934. ExFreePool(deviceIdString);
  935. }
  936. }
  937. return status;
  938. }
  939. PWSTR
  940. ParGetPortLptName(
  941. IN PDEVICE_OBJECT PortDeviceObject
  942. )
  943. // return 0 on any error
  944. {
  945. NTSTATUS status;
  946. PARALLEL_PNP_INFORMATION pnpInfo;
  947. // Get Parallel Pnp Info from ParPort device, return PortName
  948. status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_GET_PARALLEL_PNP_INFO,
  949. PortDeviceObject, NULL, 0,
  950. &pnpInfo, sizeof(PARALLEL_PNP_INFORMATION), NULL);
  951. if( NT_SUCCESS(status) ) {
  952. return (PWSTR)(pnpInfo.PortName);
  953. } else {
  954. ParDump2(PARERRORS, ("devobj::ParGetPortLptName - FAILED - returning 0\n"));
  955. return 0;
  956. }
  957. }
  958. UCHAR
  959. ParGet1284_3DeviceCount(
  960. IN PDEVICE_OBJECT PortDeviceObject
  961. )
  962. // return 0 on any error
  963. {
  964. NTSTATUS status;
  965. PARALLEL_PNP_INFORMATION pnpInfo;
  966. // Get Parallel Pnp Info from ParPort device, return Dot3 device Id count returned
  967. status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_GET_PARALLEL_PNP_INFO,
  968. PortDeviceObject, NULL, 0,
  969. &pnpInfo, sizeof(PARALLEL_PNP_INFORMATION), NULL);
  970. if( NT_SUCCESS(status) ) {
  971. return (UCHAR)(pnpInfo.Ieee1284_3DeviceCount);
  972. } else {
  973. ParDump2(PARERRORS, ("devobj::ParGet1284_3DeviceCount - FAILED - returning 0\n"));
  974. return 0;
  975. }
  976. }
  977. NTSTATUS
  978. ParBuildSendInternalIoctl(
  979. IN ULONG IoControlCode,
  980. IN PDEVICE_OBJECT TargetDeviceObject,
  981. IN PVOID InputBuffer OPTIONAL,
  982. IN ULONG InputBufferLength,
  983. OUT PVOID OutputBuffer OPTIONAL,
  984. IN ULONG OutputBufferLength,
  985. IN PLARGE_INTEGER RequestedTimeout OPTIONAL
  986. )
  987. /*++dvdf
  988. Routine Description:
  989. This routine builds and sends an Internal IOCTL to the TargetDeviceObject, waits
  990. for the IOCTL to complete, and returns status to the caller.
  991. *** WORKWORK - dvdf 12Dec98: This function does not support Input and Output in the same IOCTL
  992. Arguments:
  993. IoControlCode - the IOCTL to send
  994. TargetDeviceObject - who to send the IOCTL to
  995. InputBuffer - pointer to input buffer, if any
  996. InputBufferLength, - length of input buffer
  997. OutputBuffer - pointer to output buffer, if any
  998. OutputBufferLength, - length of output buffer
  999. Timeout - how long to wait for request to complete, NULL==use driver global AcquirePortTimeout
  1000. Return Value:
  1001. Status
  1002. --*/
  1003. {
  1004. NTSTATUS status;
  1005. PIRP irp;
  1006. LARGE_INTEGER timeout;
  1007. KEVENT event;
  1008. PIO_STACK_LOCATION irpSp;
  1009. BOOLEAN needToCopyOutputBuffer = FALSE;
  1010. PAGED_CODE();
  1011. ParDump2(PARIOCTL1,("devobj::ParBuildSendInternalIoctl: Enter\n"));
  1012. //
  1013. // Current limitation is that this function does not handle a request with
  1014. // both InputBufferLength and OutputBufferLength > 0
  1015. //
  1016. if( InputBufferLength != 0 && OutputBufferLength != 0 ) {
  1017. // ASSERTMSG("ParBuildSendInternalIoctl does not support input and output in the same IOCTL \n", FALSE);
  1018. return STATUS_UNSUCCESSFUL;
  1019. }
  1020. //
  1021. // Allocate and initialize IRP
  1022. //
  1023. irp = IoAllocateIrp( (CCHAR)(TargetDeviceObject->StackSize + 1), FALSE );
  1024. if( !irp ) {
  1025. return STATUS_INSUFFICIENT_RESOURCES;
  1026. }
  1027. irpSp = IoGetNextIrpStackLocation( irp );
  1028. irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  1029. irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
  1030. irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
  1031. irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
  1032. if( InputBufferLength != 0 ) {
  1033. irp->AssociatedIrp.SystemBuffer = InputBuffer;
  1034. } else if( OutputBufferLength != 0 ) {
  1035. irp->AssociatedIrp.SystemBuffer = OutputBuffer;
  1036. }
  1037. //
  1038. // Set completion routine and send IRP
  1039. //
  1040. KeInitializeEvent( &event, NotificationEvent, FALSE );
  1041. IoSetCompletionRoutine( irp, ParSynchCompletionRoutine, &event, TRUE, TRUE, TRUE );
  1042. status = ParCallDriver(TargetDeviceObject, irp);
  1043. if( !NT_SUCCESS(status) ) {
  1044. ParDump2(PARIOCTL1,("devobj::ParBuildSendInternalIoctl: ParCallDriver FAILED - status=%x\n",status));
  1045. IoFreeIrp( irp );
  1046. return status;
  1047. }
  1048. //
  1049. // Set timeout and wait
  1050. //
  1051. // user specified : default
  1052. timeout = (NULL != RequestedTimeout) ? *RequestedTimeout : AcquirePortTimeout;
  1053. status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
  1054. //
  1055. // Did we timeout or did the IRP complete?
  1056. //
  1057. if( status == STATUS_TIMEOUT ) {
  1058. // we timed out - cancel the IRP
  1059. IoCancelIrp( irp );
  1060. KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  1061. }
  1062. //
  1063. // Irp is complete, grab the status and free the irp
  1064. //
  1065. status = irp->IoStatus.Status;
  1066. IoFreeIrp( irp );
  1067. return status;
  1068. }
  1069. NTSTATUS
  1070. ParSelect1284_3Device(
  1071. IN PDEVICE_OBJECT PortDeviceObject,
  1072. IN UCHAR Dot3DeviceId
  1073. )
  1074. /*++dvdf
  1075. Routine Description:
  1076. This routine selects a 1284.3 daisy chain device via an
  1077. IOCTL_INTERNAL_SELECT_DEVICE sent to the ParPort to which
  1078. our device is connected.
  1079. Note: Caller must have already Acquired the Port prior to calling this function.
  1080. Arguments:
  1081. PortDeviceObject - points to the ParPort that the device is connected to.
  1082. Dot3DeviceId - IEEE 1284.3 daisy chain id (in the range [0..3]) to be selected.
  1083. Return Value:
  1084. STATUS_SUCCESS - if the device was selected
  1085. !STATUS_SUCCESS - otherwise
  1086. --*/
  1087. {
  1088. PARALLEL_1284_COMMAND par1284Command;
  1089. par1284Command.ID = Dot3DeviceId;
  1090. par1284Command.Port = 0;
  1091. par1284Command.CommandFlags = PAR_HAVE_PORT_KEEP_PORT; // we have already Acquired the port
  1092. return ParBuildSendInternalIoctl(IOCTL_INTERNAL_SELECT_DEVICE,
  1093. PortDeviceObject,
  1094. &par1284Command, sizeof(PARALLEL_1284_COMMAND),
  1095. NULL, 0, NULL);
  1096. }
  1097. NTSTATUS
  1098. ParDeselect1284_3Device(
  1099. IN PDEVICE_OBJECT PortDeviceObject,
  1100. IN UCHAR Dot3DeviceId
  1101. )
  1102. /*++dvdf
  1103. Routine Description:
  1104. This routine selects a 1284.3 daisy chain device via an
  1105. IOCTL_INTERNAL_SELECT_DEVICE sent to the ParPort to which
  1106. our device is connected.
  1107. Note: This function does not Release the port so the Caller still has
  1108. the port after this function returns.
  1109. Arguments:
  1110. PortDeviceObject - points to the ParPort that the device is connected to.
  1111. Dot3DeviceId - IEEE 1284.3 daisy chain id (in the range [0..3]) to be selected.
  1112. Return Value:
  1113. STATUS_SUCCESS - if the device was selected
  1114. !STATUS_SUCCESS - otherwise
  1115. --*/
  1116. {
  1117. PARALLEL_1284_COMMAND par1284Command;
  1118. par1284Command.ID = Dot3DeviceId;
  1119. par1284Command.Port = 0;
  1120. par1284Command.CommandFlags = PAR_HAVE_PORT_KEEP_PORT; // just Deselect device, don't Release port
  1121. return ParBuildSendInternalIoctl(IOCTL_INTERNAL_DESELECT_DEVICE,
  1122. PortDeviceObject,
  1123. &par1284Command, sizeof(PARALLEL_1284_COMMAND),
  1124. NULL, 0, NULL);
  1125. }