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.

2615 lines
88 KiB

  1. #include "pch.h"
  2. VOID
  3. P5WorkItemFreePort( PDEVICE_OBJECT Fdo, PFDO_EXTENSION Fdx ) {
  4. PIO_WORKITEM workItem;
  5. UNREFERENCED_PARAMETER( Fdo );
  6. workItem = InterlockedExchangePointer( &Fdx->FreePortWorkItem, NULL );
  7. if( workItem ) {
  8. IoFreeWorkItem( workItem );
  9. }
  10. PptFreePort( Fdx );
  11. }
  12. BOOLEAN
  13. P5SelectDaisyChainDevice(
  14. IN PUCHAR Controller,
  15. IN UCHAR DeviceId
  16. )
  17. {
  18. const ULONG maxRetries = 4;
  19. ULONG retryCount = 0;
  20. BOOLEAN selected = FALSE;
  21. DD(NULL,DDE,"P5SelectDaisyChainDevice %x %d\n",Controller,DeviceId);
  22. while( !selected && retryCount < maxRetries ) {
  23. selected = PptSend1284_3Command( Controller, (UCHAR)(CPP_SELECT | DeviceId) );
  24. ++retryCount;
  25. }
  26. return selected;
  27. }
  28. BOOLEAN
  29. P5DeselectAllDaisyChainDevices(
  30. IN PUCHAR Controller
  31. )
  32. {
  33. const ULONG maxRetries = 4;
  34. ULONG retryCount = 0;
  35. BOOLEAN deselected = FALSE;
  36. DD(NULL,DDE,"P5DeselectAllDaisyChainDevices %x\n",Controller);
  37. while( !deselected && retryCount < maxRetries ) {
  38. deselected = PptSend1284_3Command( Controller, (UCHAR)CPP_DESELECT );
  39. ++retryCount;
  40. }
  41. return deselected;
  42. }
  43. VOID
  44. P5DeletePdoSymLink(
  45. IN PDEVICE_OBJECT Pdo
  46. )
  47. //
  48. // clean up symbolic link so we can reuse it immediately for a new PDO
  49. //
  50. {
  51. PPDO_EXTENSION pdx = Pdo->DeviceExtension;
  52. if( pdx->SymLinkName ) {
  53. UNICODE_STRING uniSymLinkName;
  54. NTSTATUS status;
  55. DD((PCE)pdx,DDE,"P5DeletePdoSymLink\n");
  56. RtlInitUnicodeString( &uniSymLinkName, pdx->SymLinkName );
  57. status = IoDeleteSymbolicLink( &uniSymLinkName );
  58. PptAssert( STATUS_SUCCESS == status );
  59. ExFreePool( pdx->SymLinkName );
  60. pdx->SymLinkName = NULL;
  61. }
  62. return;
  63. }
  64. VOID
  65. P5MarkPdoAsHardwareGone(
  66. IN PDEVICE_OBJECT Fdo,
  67. IN enum _PdoType PdoType,
  68. IN ULONG DaisyChainId OPTIONAL // ignored if PdoType != PdoTypeDaisyChain
  69. )
  70. {
  71. PFDO_EXTENSION fdx = Fdo->DeviceExtension;
  72. PPDO_EXTENSION pdx;
  73. PDEVICE_OBJECT pdo;
  74. switch( PdoType ) {
  75. case PdoTypeRawPort:
  76. DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeRawPort\n");
  77. pdo = fdx->RawPortPdo;
  78. fdx->RawPortPdo = NULL;
  79. break;
  80. case PdoTypeEndOfChain:
  81. DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeEndOfChain\n");
  82. pdo = fdx->EndOfChainPdo;
  83. fdx->EndOfChainPdo = NULL;
  84. break;
  85. case PdoTypeDaisyChain:
  86. PptAssert( (0 == DaisyChainId) || (1 == DaisyChainId) );
  87. DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeDaisyChain - %d\n",DaisyChainId);
  88. pdo = fdx->DaisyChainPdo[ DaisyChainId ];
  89. fdx->DaisyChainPdo[ DaisyChainId ] = NULL;
  90. break;
  91. case PdoTypeLegacyZip:
  92. DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeLegacyZip\n");
  93. pdo = fdx->LegacyZipPdo;
  94. fdx->LegacyZipPdo = NULL;
  95. break;
  96. default:
  97. DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - Invalid PdoType parameter\n",FALSE);
  98. PptAssertMsg("P5MarkPdoAsHardwareGone - Invalid PdoType parameter",FALSE);
  99. return;
  100. }
  101. pdx = pdo->DeviceExtension;
  102. P5DeletePdoSymLink( pdo );
  103. InsertTailList( &fdx->DevDeletionListHead, &pdx->DevDeletionList );
  104. pdx->DeleteOnRemoveOk = TRUE;
  105. return;
  106. }
  107. BOOLEAN
  108. P5IsDeviceStillThere(
  109. IN PDEVICE_OBJECT Fdo,
  110. IN PDEVICE_OBJECT Pdo
  111. )
  112. //
  113. // Is the Pdo device still connected to the port represented by the Fdo?
  114. //
  115. // N.B. Fdo must own (have locked for exclusive access) the port before calling this function
  116. // or we can corrupt the data stream and hang devices connected to the port
  117. //
  118. {
  119. PFDO_EXTENSION fdx = Fdo->DeviceExtension;
  120. PPDO_EXTENSION pdx = Pdo->DeviceExtension;
  121. BOOLEAN deviceStillThere = FALSE;
  122. PCHAR devIdString = NULL;
  123. PUCHAR controller = fdx->PortInfo.Controller;
  124. PptAssert( DevTypeFdo == fdx->DevType );
  125. PptAssert( DevTypePdo == pdx->DevType );
  126. //
  127. // Select device if needed, pull a fresh 1284 device ID string
  128. // from the device, and compare the Mfg and Mdl from the fresh
  129. // device ID with those stored in our extension. If the Mfg and
  130. // Mdl fields match then the device is still there.
  131. //
  132. switch( pdx->PdoType ) {
  133. case PdoTypeRawPort:
  134. // raw port is always present - it's a virtual device
  135. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeRawPort - StillThere\n");
  136. deviceStillThere = TRUE;
  137. break;
  138. case PdoTypeLegacyZip:
  139. deviceStillThere = P5LegacyZipDetected( fdx->PortInfo.Controller );
  140. if( deviceStillThere ) {
  141. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeLegacyZip - StillThere\n");
  142. } else {
  143. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeLegacyZip - Gone\n");
  144. }
  145. break;
  146. case PdoTypeDaisyChain:
  147. //
  148. // Select device, pull a fresh 1284 device ID string
  149. // from the device, and compare the Mfg and Mdl from the fresh
  150. // device ID with those stored in our extension. If the Mfg and
  151. // Mdl fields match then the device is still there.
  152. //
  153. {
  154. UCHAR daisyChainId = pdx->Ieee1284_3DeviceId;
  155. // select device
  156. if( P5SelectDaisyChainDevice( controller, daisyChainId ) ) {
  157. BOOLEAN bBuildStlDeviceId = FALSE;
  158. PPDO_EXTENSION dummyPdx = NULL;
  159. devIdString = NULL;
  160. // do a check to see if this is an SCM Micro device
  161. dummyPdx = ExAllocatePool( PagedPool, sizeof(PDO_EXTENSION) );
  162. if( dummyPdx != NULL ) {
  163. RtlZeroMemory( dummyPdx, sizeof(PDO_EXTENSION) );
  164. dummyPdx->Controller = fdx->PortInfo.Controller;
  165. bBuildStlDeviceId = ParStlCheckIfStl( dummyPdx, daisyChainId );
  166. if( bBuildStlDeviceId ) {
  167. // SCM Micro device
  168. ULONG DeviceIdSize;
  169. devIdString = ParStlQueryStlDeviceId( dummyPdx, NULL, 0,&DeviceIdSize, TRUE );
  170. } else {
  171. // non-SCM Micro device
  172. devIdString = P4ReadRawIeee1284DeviceId( controller );
  173. }
  174. ExFreePool( dummyPdx );
  175. }
  176. if( devIdString ) {
  177. // got a 1284 device ID string from the device
  178. PCHAR mfg, mdl, cls, des, aid, cid;
  179. ParPnpFindDeviceIdKeys( &mfg, &mdl, &cls, &des, &aid, &cid, devIdString+2 );
  180. if( mfg && mdl ) {
  181. // we have a device, is it the same device?
  182. if( (0 == strcmp( mfg, pdx->Mfg )) && (0 == strcmp( mdl, pdx->Mdl )) ) {
  183. // same device
  184. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - StillThere\n",daisyChainId);
  185. deviceStillThere = TRUE;
  186. } else {
  187. // different device - IDs don't match
  188. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - diff 1284 ID\n",daisyChainId);
  189. deviceStillThere = FALSE;
  190. }
  191. } else {
  192. // either mfg or mdl field not found
  193. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - bad 1284 ID\n",daisyChainId);
  194. deviceStillThere = FALSE;
  195. }
  196. // don't forget to free temp pool
  197. ExFreePool( devIdString );
  198. } else {
  199. // unable to get a 1284 device ID string from the device
  200. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - no 1284 ID\n",daisyChainId);
  201. deviceStillThere = FALSE;
  202. }
  203. // don't forget to deselect device
  204. P5DeselectAllDaisyChainDevices( controller );
  205. } else {
  206. // unable to select device
  207. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - unable to select\n",daisyChainId);
  208. deviceStillThere = FALSE;
  209. }
  210. } // end new scope for case PdoTypeDaisyChain
  211. break;
  212. case PdoTypeEndOfChain:
  213. //
  214. // Pull a fresh 1284 device ID string from the device, and
  215. // compare the Mfg and Mdl from the fresh device ID with
  216. // those stored in our extension. If the Mfg and Mdl
  217. // fields match then the device is still there.
  218. //
  219. {
  220. ULONG tryNumber = 0;
  221. const ULONG maxTries = 5; // arbitrary number
  222. do {
  223. ++tryNumber;
  224. devIdString = P4ReadRawIeee1284DeviceId( controller );
  225. if( devIdString ) {
  226. PCHAR mfg, mdl, cls, des, aid, cid;
  227. ParPnpFindDeviceIdKeys( &mfg, &mdl, &cls, &des, &aid, &cid, devIdString+2 );
  228. if( mfg && mdl ) {
  229. // we have a device, is it the same device?
  230. if( (0 == strcmp( mfg, pdx->Mfg )) && (0 == strcmp( mdl, pdx->Mdl )) ) {
  231. // same device
  232. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - StillThere\n");
  233. deviceStillThere = TRUE;
  234. } else {
  235. // different device - IDs don't match
  236. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - Gone - diff 1284 ID\n");
  237. deviceStillThere = FALSE;
  238. }
  239. } else {
  240. // either mfg or mdl field not found
  241. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - Gone - bad 1284 ID\n");
  242. deviceStillThere = FALSE;
  243. }
  244. // don't forget to free temp pool
  245. ExFreePool( devIdString );
  246. } else {
  247. // unable to get a 1284 device ID string from the device
  248. DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - Gone - no 1284 ID\n");
  249. deviceStillThere = FALSE;
  250. }
  251. if( (FALSE == deviceStillThere ) && (PASSIVE_LEVEL == KeGetCurrentIrql()) ) {
  252. LARGE_INTEGER delay;
  253. delay.QuadPart = - 10 * 1000 * 120; // 120 ms - 3x the usual arbitrary delay
  254. KeDelayExecutionThread( KernelMode, FALSE, &delay);
  255. }
  256. } while( (FALSE == deviceStillThere) && (tryNumber < maxTries) );
  257. }
  258. break;
  259. default:
  260. PptAssertMsg("P5IsDeviceStillThere - invalid PdoType",FALSE);
  261. DD((PCE)Fdo,DDE,"P5IsDeviceStillThere - invalid PdoType\n");
  262. deviceStillThere = TRUE; // don't know what to do here - so, guess
  263. }
  264. return deviceStillThere;
  265. }
  266. NTSTATUS
  267. PptAcquirePortViaIoctl(
  268. IN PDEVICE_OBJECT PortDeviceObject,
  269. IN PLARGE_INTEGER Timeout OPTIONAL
  270. )
  271. /*++dvdf
  272. Routine Description:
  273. This routine acquires the specified parallel port from the parallel
  274. port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE.
  275. Arguments:
  276. PortDeviceObject - points to the ParPort device to be acquired
  277. Return Value:
  278. STATUS_SUCCESS - if the port was successfully acquired
  279. !STATUS_SUCCESS - otherwise
  280. --*/
  281. {
  282. LARGE_INTEGER localTimeout;
  283. if( Timeout ) {
  284. localTimeout = *Timeout; // caller specified
  285. } else {
  286. localTimeout = AcquirePortTimeout; // driver global variable default
  287. }
  288. return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE,
  289. PortDeviceObject, NULL, 0, NULL, 0, &localTimeout);
  290. }
  291. NTSTATUS
  292. PptReleasePortViaIoctl(
  293. IN PDEVICE_OBJECT PortDeviceObject
  294. )
  295. /*++dvdf
  296. Routine Description:
  297. This routine releases the specified parallel port back to the the parallel
  298. port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_FREE.
  299. Arguments:
  300. PortDeviceObject - points to the ParPort device to be released
  301. Return Value:
  302. STATUS_SUCCESS - if the port was successfully released
  303. !STATUS_SUCCESS - otherwise
  304. --*/
  305. {
  306. return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_FREE,
  307. PortDeviceObject, NULL, 0, NULL, 0, NULL);
  308. }
  309. VOID
  310. PptWriteMfgMdlToDevNode(
  311. IN PDEVICE_OBJECT Pdo,
  312. IN PCHAR Mfg,
  313. IN PCHAR Mdl
  314. )
  315. {
  316. PPDO_EXTENSION pdx = Pdo->DeviceExtension;
  317. if( Mfg && Mdl ) {
  318. NTSTATUS status;
  319. HANDLE handle;
  320. LONG mfgLen = strlen( Mfg );
  321. LONG mdlLen = strlen( Mdl );
  322. LONG maxLen = mfgLen > mdlLen ? mfgLen : mdlLen;
  323. LONG bufLen = ( maxLen + sizeof(CHAR) ) * sizeof(WCHAR);
  324. PWSTR buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
  325. if( buffer ) {
  326. status = IoOpenDeviceRegistryKey( Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &handle );
  327. if( STATUS_SUCCESS == status ) {
  328. UNICODE_STRING uniValueName;
  329. LONG wcharCount;
  330. //
  331. // Write MFG to DevNode
  332. //
  333. RtlInitUnicodeString( &uniValueName, L"IEEE_1284_Manufacturer" );
  334. wcharCount = _snwprintf( buffer, bufLen/sizeof(WCHAR), L"%S", Mfg );
  335. if( (wcharCount > 0) && (wcharCount < (LONG)(bufLen/sizeof(WCHAR))) ){
  336. // no buffer overflow - continue
  337. status = ZwSetValueKey( handle, &uniValueName, 0, REG_SZ, buffer, (wcharCount+1)*sizeof(WCHAR) );
  338. PptAssert( STATUS_SUCCESS == status );
  339. } else {
  340. // buffer overflow - skip writing this value to devnode
  341. PptAssert(!"PptWriteMfgMdlToDevNode - buffer overflow on Mfg");
  342. DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - buffer overflow on Mfg\n");
  343. }
  344. //
  345. // Write MDL to DevNode
  346. //
  347. RtlInitUnicodeString( &uniValueName, L"IEEE_1284_Model" );
  348. wcharCount = _snwprintf( buffer, bufLen/sizeof(WCHAR), L"%S", Mdl );
  349. if( (wcharCount > 0) && (wcharCount < (LONG)(bufLen/sizeof(WCHAR))) ){
  350. // no buffer overflow - continue
  351. status = ZwSetValueKey( handle, &uniValueName, 0, REG_SZ, buffer, (wcharCount+1)*sizeof(WCHAR) );
  352. PptAssert( STATUS_SUCCESS == status );
  353. } else {
  354. // buffer overflow - skip writing this value to devnode
  355. PptAssert(!"PptWriteMfgMdlToDevNode - buffer overflow on Mdl");
  356. DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - buffer overflow on Mdl\n");
  357. }
  358. ZwClose( handle );
  359. } else {
  360. DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - IoOpenDeviceRegistryKey FAILED - status = %x\n",status);
  361. }
  362. ExFreePool( buffer );
  363. } // end if( buffer )
  364. } else {
  365. PptAssert(!"PptWriteMfgMdlToDevNode - Mfg or Mdl is NULL - calling function should catch this!");
  366. DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - Mfg or Mdl is NULL - calling function should catch this!");
  367. }
  368. }
  369. NTSTATUS
  370. PptFdoHandleBusRelations(
  371. IN PDEVICE_OBJECT Fdo,
  372. IN PIRP Irp
  373. )
  374. {
  375. PFDO_EXTENSION fdx = Fdo->DeviceExtension;
  376. ULONG deviceCount = 0;
  377. ULONG daisyChainDevCount;
  378. PDEVICE_RELATIONS devRel;
  379. ULONG devRelSize;
  380. NTSTATUS status;
  381. LARGE_INTEGER acquirePortTimeout;
  382. BOOLEAN acquiredPort;
  383. PUCHAR controller = fdx->PortInfo.Controller;
  384. BOOLEAN changeDetected;
  385. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - enter\n");
  386. //
  387. // acquire exclusive access to bus
  388. //
  389. // timeout is in 100 ns units
  390. acquirePortTimeout.QuadPart = -(10 * 1000 * 1000 * 2); // 2 seconds
  391. // RMT - is it valid to send this IOCTL to FDO from here?
  392. status = PptAcquirePortViaIoctl( Fdo, &acquirePortTimeout );
  393. if( STATUS_SUCCESS == status ) {
  394. // we have the port
  395. acquiredPort = TRUE;
  396. } else {
  397. // failed to aquire port
  398. acquiredPort = FALSE;
  399. // skip rescanning port - just report same thing we reported during previous scan
  400. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - failed to acquire port for rescan\n");
  401. goto target_failed_to_acquire_port;
  402. }
  403. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Port Acquired\n");
  404. //
  405. // Rescan the bus, note changes, create new PDOs or mark existing
  406. // PDOs for removal as required
  407. //
  408. //
  409. // Handle Raw Port Legacy Interface LPTx device
  410. //
  411. if( !fdx->RawPortPdo ) {
  412. // first time through this - create our LPTx legacy interface PDO
  413. DD((PCE)fdx,DDT,"PptFdoHandleBusRelations - attempting to create RawPortPdo\n");
  414. fdx->RawPortPdo = P4CreatePdo( Fdo, PdoTypeRawPort, 0, NULL );
  415. }
  416. //
  417. // Handle End of Chain Device
  418. //
  419. // make sure all 1284.3 daisy chain devices are deselected
  420. P5DeselectAllDaisyChainDevices( controller );
  421. {
  422. // A small delay here seems to improve reliablility of 1284 device ID queries below.
  423. LARGE_INTEGER delay;
  424. delay.QuadPart = -1;
  425. KeDelayExecutionThread( KernelMode, FALSE, &delay );
  426. }
  427. if( fdx->EndOfChainPdo ) {
  428. if( fdx->DisableEndOfChainBusRescan ) {
  429. //
  430. // Pretend that the LPTx.4 device from previous rescan is still present.
  431. //
  432. // This is needed to work around firmware state machines that can't handle a
  433. // 1284 Device ID query while a print job is active.
  434. //
  435. ; // do nothing
  436. } else {
  437. //
  438. // we had an end of chain device - verify that it's still there
  439. //
  440. if( !P5IsDeviceStillThere( Fdo, fdx->EndOfChainPdo ) ) {
  441. // End of chain device is gone - do some cleanup and mark the PDO for removal/deletion
  442. DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - EndOfChain device gone\n");
  443. // note - P5MarkPdoAsHardwareGone sets fdx->EndOfChainPdo to NULL
  444. P5MarkPdoAsHardwareGone( Fdo, PdoTypeEndOfChain, 0 );
  445. }
  446. }
  447. }
  448. if( NULL == fdx->EndOfChainPdo ) {
  449. //
  450. // we don't have an EndOfChain device - check for EndOfChain device arrival
  451. //
  452. PCHAR devId = P4ReadRawIeee1284DeviceId( controller );
  453. if( devId ) {
  454. // RawIeee1284 string includes 2 bytes of length data at beginning, omit these 2 bytes in call to P4CreatePdo
  455. PDEVICE_OBJECT EndOfChainPdo = P4CreatePdo( Fdo, PdoTypeEndOfChain, 0, (devId+2) );
  456. DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - EndOfChain device detected <%s>\n",(devId+2));
  457. if( EndOfChainPdo ) {
  458. fdx->EndOfChainPdo = EndOfChainPdo;
  459. DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - created EndOfChainPdo\n");
  460. } else {
  461. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - FAILED to create EndOfChainPdo\n");
  462. }
  463. ExFreePool( devId );
  464. }
  465. }
  466. #ifdef _X86_ // Zip drives not supported on 64bit systems
  467. //
  468. // Handle Legacy Zip device
  469. //
  470. if( fdx->LegacyZipPdo ) {
  471. //
  472. // we had a Legacy Zip device - verify that it's still there
  473. //
  474. if( !P5IsDeviceStillThere( Fdo, fdx->LegacyZipPdo ) ) {
  475. // Legacy Zip device is gone - do some cleanup and mark the PDO for removal/deletion
  476. DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - LegacyZip device gone\n");
  477. // note - P5MarkPdoAsHardwareGone sets fdx->LegacyZipPdo to NULL
  478. P5MarkPdoAsHardwareGone( Fdo, PdoTypeLegacyZip, 0 );
  479. }
  480. }
  481. if( NULL == fdx->LegacyZipPdo ) {
  482. //
  483. // We don't have a LegacyZip - check for arrival
  484. //
  485. if( !ParEnableLegacyZip ) {
  486. //
  487. // Enumeration of LegacyZip drives was disabled, check the
  488. // registry to see if user has enabled LegacyZip detection
  489. //
  490. // Check under \HKLM\SYSTEM\CCS\Services\Parport\Parameters
  491. PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"ParEnableLegacyZip", &ParEnableLegacyZip );
  492. if( !ParEnableLegacyZip ) {
  493. // Check under \HKLM\SYSTEM\CCS\Services\Parallel\Parameters (upgrade case - under Win2k flag was here)
  494. PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parallel\\Parameters", L"ParEnableLegacyZip", &ParEnableLegacyZip );
  495. if( ParEnableLegacyZip ) {
  496. // we found the setting in the old location, save
  497. // setting in new Parport location so that we find the
  498. // flag on the first check in the future
  499. PptRegSetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"ParEnableLegacyZip", &ParEnableLegacyZip );
  500. }
  501. }
  502. }
  503. if( ParEnableLegacyZip ) {
  504. //
  505. // Enumeration of LegacyZip drives is enabled - check for a LegacyZip arrival
  506. //
  507. if( P5LegacyZipDetected( controller ) ) {
  508. // detected drive - create LegacyZip PDO
  509. PDEVICE_OBJECT legacyZipPdo = P4CreatePdo( Fdo, PdoTypeLegacyZip, 0, NULL );
  510. DD((PCE)fdx,DDE,"legacy Zip arrival detected\n");
  511. if( legacyZipPdo ) {
  512. fdx->LegacyZipPdo = legacyZipPdo;
  513. DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - created LegacyZipPdo\n");
  514. } else {
  515. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - FAILED to create LegacyZipPdo\n");
  516. }
  517. } else {
  518. // no legacy Zip detected - nothing more to do here
  519. DD((PCE)fdx,DDE,"no legacy Zip detected\n");
  520. }
  521. } // if( ParEnableLegacyZip ) -- Detection of LegacyZips is enabled
  522. } // if( fdx->LegacyZipPdo )
  523. //
  524. // Handle enumeration of IEEE 1284.3 Daisy Chain Devices
  525. //
  526. // did the 1284.3 daisy chain change since the last rescan?
  527. daisyChainDevCount = PptInitiate1284_3( fdx );
  528. DD((PCE)fdx,DDW,"daisyChainDevCount = %d\n",daisyChainDevCount);
  529. changeDetected = FALSE;
  530. {
  531. ULONG id;
  532. const ULONG maxId = 1;
  533. ULONG count = 0;
  534. for( id = 0 ; id <= maxId ; ++id ) {
  535. if( fdx->DaisyChainPdo[id] ) {
  536. ++count;
  537. }
  538. }
  539. if( count != daisyChainDevCount ) {
  540. // number of devices changed from previous scan
  541. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - number of DC devices changed - count=%d, daisyChainDevCount=%d\n",
  542. count, daisyChainDevCount);
  543. changeDetected = TRUE;
  544. }
  545. }
  546. if( !changeDetected ) {
  547. // number of devices stayed the same - are any of the devices different?
  548. //
  549. // number of daisy chain devices didn't change
  550. // check if any of the devices changed
  551. ULONG id;
  552. const ULONG maxId = 1;
  553. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - number of DC devices stayed same - check the devices\n");
  554. for( id = 0 ; id <= maxId ; ++id ) {
  555. if( fdx->DaisyChainPdo[id] && !P5IsDeviceStillThere( Fdo, fdx->DaisyChainPdo[id] ) ) {
  556. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - a DC device changed\n");
  557. changeDetected = TRUE;
  558. break;
  559. }
  560. }
  561. }
  562. if( changeDetected ) {
  563. // we detected a change in the 1284.3 daisy chain devices - nuke all existing devices
  564. ULONG id;
  565. const ULONG maxId = 1;
  566. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - changeDetected - nuking existing daisy chain PDOs\n");
  567. for( id = 0 ; id <= maxId ; ++id ) {
  568. if( fdx->DaisyChainPdo[id] ) {
  569. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - nuking daisy chain %d\n",id);
  570. P5MarkPdoAsHardwareGone( Fdo, PdoTypeDaisyChain, id );
  571. PptAssert( NULL == fdx->DaisyChainPdo[id] );
  572. }
  573. }
  574. fdx->PnpInfo.Ieee1284_3DeviceCount = 0;
  575. } else {
  576. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - !changeDetected in daisy chain PDOs\n");
  577. }
  578. // reinit daisy chain and assign addresses
  579. daisyChainDevCount = PptInitiate1284_3( fdx );
  580. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - daisyChainDevCount = %d\n",daisyChainDevCount);
  581. if( daisyChainDevCount > 2 ) {
  582. // we only support 2 devices per port even though the spec supports up to 4 devices per port
  583. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - DaisyChainDevCount > 2, set to 2\n");
  584. daisyChainDevCount = 2;
  585. }
  586. if( changeDetected ) {
  587. // we detected a change in the 1284.3 daisy chain devices - we
  588. // previously nuked all old devices - now create a new PDO for
  589. // each device detected
  590. UCHAR id;
  591. PptAssert( 0 == fdx->PnpInfo.Ieee1284_3DeviceCount );
  592. for( id = 0 ; id < daisyChainDevCount ; ++id ) {
  593. BOOLEAN bBuildStlDeviceId = FALSE;
  594. PPDO_EXTENSION pdx = NULL;
  595. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - changeDetected - trying to create new daisy chain PDOs\n");
  596. if( P5SelectDaisyChainDevice( controller, id ) ) {
  597. PCHAR devId = NULL;
  598. // do a check to see if this is an SCM Micro device
  599. pdx = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, sizeof(PDO_EXTENSION) );
  600. if( pdx != NULL ) {
  601. RtlZeroMemory( pdx, sizeof(PDO_EXTENSION) );
  602. pdx->Controller = fdx->PortInfo.Controller;
  603. bBuildStlDeviceId = ParStlCheckIfStl( pdx, id );
  604. ExFreePool( pdx );
  605. }
  606. if( bBuildStlDeviceId ) {
  607. // SCM Micro device
  608. pdx = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, sizeof(PDO_EXTENSION) );
  609. if( pdx != NULL ) {
  610. ULONG DeviceIdSize;
  611. RtlZeroMemory( pdx, sizeof(PDO_EXTENSION) );
  612. pdx->Controller = fdx->PortInfo.Controller;
  613. devId = ParStlQueryStlDeviceId(pdx, NULL, 0,&DeviceIdSize, TRUE);
  614. ExFreePool (pdx);
  615. }
  616. } else {
  617. // non-SCM Micro device
  618. devId = P4ReadRawIeee1284DeviceId( controller );
  619. }
  620. if( devId ) {
  621. // try to create a PDO for the daisy chain device
  622. fdx->DaisyChainPdo[id] = P4CreatePdo( Fdo, PdoTypeDaisyChain, id, (devId+2) );
  623. if( fdx->DaisyChainPdo[id] ) {
  624. // have new PDO
  625. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - new DaisyChainPdo[%d]\n",id);
  626. ++(fdx->PnpInfo.Ieee1284_3DeviceCount);
  627. if( bBuildStlDeviceId ) {
  628. // SCM Micro device - requires additional initialization
  629. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - new SCM Micro DaisyChainPdo[%d]\n",id);
  630. pdx = fdx->DaisyChainPdo[id]->DeviceExtension;
  631. pdx->Controller = fdx->PortInfo.Controller;
  632. ParStlCheckIfStl( pdx, 0 ); // update IEEE 1284 flags in the new pdx
  633. }
  634. } else {
  635. // create PDO failed
  636. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - create DaisyChainPdo[%d] failed\n",id);
  637. }
  638. ExFreePool( devId );
  639. } else {
  640. // devId failed
  641. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - devId for DC %d failed\n",id);
  642. }
  643. P5DeselectAllDaisyChainDevices( controller );
  644. } else {
  645. // select failed
  646. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - select for DC %d failed\n",id);
  647. }
  648. }
  649. }
  650. {
  651. ULONG i;
  652. ULONG count = 0;
  653. i = 0;
  654. for( i = 0 ; i < 2 ; ++i ) {
  655. if( fdx->DaisyChainPdo[i] ) {
  656. ++count;
  657. }
  658. }
  659. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Ieee1284_3DeviceCount=%d count1 = %d\n",
  660. fdx->PnpInfo.Ieee1284_3DeviceCount,count);
  661. PptAssert( fdx->PnpInfo.Ieee1284_3DeviceCount == count );
  662. }
  663. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - daisyChainDevCount = %d, fdx->PnpInfo.Ieee1284_3DeviceCount = %d\n",
  664. daisyChainDevCount, fdx->PnpInfo.Ieee1284_3DeviceCount);
  665. // PptAssert( daisyChainDevCount == fdx->PnpInfo.Ieee1284_3DeviceCount );
  666. #endif // _X86_
  667. target_failed_to_acquire_port: // jump here if we couldn't get the port - result is that we report that nothing has changed
  668. //
  669. // Count the number of devices that we are going to report to PnP
  670. // so that we can allocate a DEVICE_RELATIONS structure of the
  671. // appropriate size.
  672. //
  673. if( fdx->RawPortPdo ) {
  674. ++deviceCount;
  675. }
  676. if( fdx->EndOfChainPdo ) {
  677. ++deviceCount;
  678. }
  679. if( fdx->LegacyZipPdo ) {
  680. ++deviceCount;
  681. }
  682. {
  683. const ULONG maxDaisyChainId = 1;
  684. ULONG i;
  685. for( i=0 ; i <= maxDaisyChainId; ++i ) {
  686. if( fdx->DaisyChainPdo[i] ) {
  687. ++deviceCount;
  688. } else {
  689. break;
  690. }
  691. }
  692. }
  693. if( deviceCount > 0 && fdx->RawPortPdo ) {
  694. //
  695. // Allocate and populate DEVICE_RELATIONS structure that we return to PnP
  696. //
  697. devRelSize = sizeof(DEVICE_RELATIONS) + (deviceCount-1)*sizeof(PDEVICE_OBJECT);
  698. devRel = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, devRelSize );
  699. if( !devRel ) {
  700. // release port and fail IRP
  701. P4ReleaseBus( Fdo );
  702. return P4CompleteRequestReleaseRemLock( Irp, STATUS_INSUFFICIENT_RESOURCES, Irp->IoStatus.Information, &fdx->RemoveLock );
  703. }
  704. { // local block - begin
  705. ULONG idx = 0;
  706. RtlZeroMemory( devRel, devRelSize );
  707. ++(devRel->Count);
  708. ObReferenceObject( fdx->RawPortPdo );
  709. devRel->Objects[ idx++ ] = fdx->RawPortPdo;
  710. if( fdx->EndOfChainPdo ) {
  711. ++(devRel->Count);
  712. ObReferenceObject( fdx->EndOfChainPdo );
  713. devRel->Objects[ idx++ ] = fdx->EndOfChainPdo;
  714. }
  715. if( fdx->LegacyZipPdo ) {
  716. ++(devRel->Count);
  717. ObReferenceObject( fdx->LegacyZipPdo );
  718. devRel->Objects[ idx++ ] = fdx->LegacyZipPdo;
  719. }
  720. {
  721. const ULONG maxDaisyChainId = 3;
  722. ULONG i;
  723. for( i=0 ; i <= maxDaisyChainId; ++i ) {
  724. if( fdx->DaisyChainPdo[i] ) {
  725. ++(devRel->Count);
  726. ObReferenceObject( fdx->DaisyChainPdo[i] );
  727. devRel->Objects[ idx++ ] = fdx->DaisyChainPdo[i];
  728. } else {
  729. break;
  730. }
  731. }
  732. }
  733. } // local block - end
  734. PptAssert( deviceCount == devRel->Count ); // verify that our two counts match
  735. DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - reporting %d devices\n",devRel->Count);
  736. Irp->IoStatus.Status = STATUS_SUCCESS;
  737. Irp->IoStatus.Information = (ULONG_PTR)devRel;
  738. } else {
  739. // deviceCount <= 0 - error somewhere - likely two ports
  740. // have the same LPTx name in the FDO stack's devnode
  741. // RMT - this assert needs to be changed to ErrorLog msg
  742. PptAssert(!"no RawPort device - likely multiple ports have same LPTx name - email: DFritz");
  743. }
  744. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - passing IRP down stack\n");
  745. status = PptPnpPassThroughPnpIrpAndReleaseRemoveLock( fdx, Irp );
  746. //
  747. // Release our lock on the bus and pass Irp down the stack
  748. //
  749. if( acquiredPort ) {
  750. PIO_WORKITEM workItem = IoAllocateWorkItem( Fdo );
  751. if( workItem ) {
  752. PIO_WORKITEM oldWorkItem = InterlockedCompareExchangePointer( &fdx->FreePortWorkItem, workItem, NULL );
  753. if( NULL == oldWorkItem ) {
  754. // no workitem currently in use, queue this one
  755. IoQueueWorkItem( workItem, P5WorkItemFreePort, DelayedWorkQueue, fdx );
  756. } else {
  757. // there is already a workitem in use, bail out and recover as best we can
  758. // We really shouldn't be able to get here - how in blazes did we
  759. // acquire the port at the top of this function if the workitem
  760. // that we queued to free the port during the previous invocation
  761. // of this function has not yet freed the port?
  762. PptAssertMsg( "workitem collision - port arbitration state may be hosed", (oldWorkItem != NULL) );
  763. IoFreeWorkItem( workItem );
  764. PptFreePort( fdx );
  765. }
  766. } else {
  767. PptFreePort( fdx );
  768. }
  769. // DbgPrint("xxx work item to free port has been queued\n");
  770. //DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Releasing Port\n");
  771. //PptFreePort( fdx );
  772. //DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Port Released\n");
  773. } else {
  774. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Port Not acquired so no need to release\n");
  775. }
  776. DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - exit\n");
  777. return status;
  778. }
  779. NTSTATUS
  780. PptPnpStartScanPciCardCmResourceList(
  781. IN PFDO_EXTENSION Fdx,
  782. IN PIRP Irp,
  783. OUT PBOOLEAN FoundPort,
  784. OUT PBOOLEAN FoundIrq,
  785. OUT PBOOLEAN FoundDma
  786. )
  787. /*++dvdf3
  788. Routine Description:
  789. This routine is used to parse the resource list for what we
  790. believe are PCI parallel port cards.
  791. This function scans the CM_RESOURCE_LIST supplied with the Pnp
  792. IRP_MN_START_DEVICE IRP, extracts the resources from the list,
  793. and saves them in the device extension.
  794. Arguments:
  795. Fdx - The device extension of the target of the START IRP
  796. Irp - The IRP
  797. FoundPort - Did we find a Port resource?
  798. FoundIrq - Did we find an IRQ resource?
  799. FoundDma - Did we find a DMA resource?
  800. Return Value:
  801. STATUS_SUCCESS - if we were given a resource list,
  802. STATUS_INSUFFICIENT_RESOURCES - otherwise
  803. --*/
  804. {
  805. NTSTATUS status = STATUS_SUCCESS;
  806. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
  807. PCM_RESOURCE_LIST ResourceList;
  808. PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
  809. PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
  810. PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor;
  811. ULONG i;
  812. ULONG length;
  813. *FoundPort = FALSE;
  814. *FoundIrq = FALSE;
  815. *FoundDma = FALSE;
  816. ResourceList = irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
  817. FullResourceDescriptor = &ResourceList->List[0];
  818. if( FullResourceDescriptor ) {
  819. Fdx->InterfaceType = FullResourceDescriptor->InterfaceType;
  820. PartialResourceList = &FullResourceDescriptor->PartialResourceList;
  821. for (i = 0; i < PartialResourceList->Count; i++) {
  822. PartialResourceDescriptor = &PartialResourceList->PartialDescriptors[i];
  823. switch (PartialResourceDescriptor->Type) {
  824. case CmResourceTypePort:
  825. length = PartialResourceDescriptor->u.Port.Length;
  826. //
  827. // Use a heuristic based on length to guess which register set is
  828. // SPP+EPP, which is ECP, and which is PCI Config or other.
  829. //
  830. switch( length ) {
  831. case 8: // SPP + EPP base address
  832. Fdx->PortInfo.OriginalController = PartialResourceDescriptor->u.Port.Start;
  833. Fdx->PortInfo.SpanOfController = PartialResourceDescriptor->u.Port.Length;
  834. Fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)Fdx->PortInfo.OriginalController.QuadPart;
  835. Fdx->AddressSpace = PartialResourceDescriptor->Flags;
  836. *FoundPort = TRUE;
  837. break;
  838. case 4: // ECP base address
  839. Fdx->PnpInfo.OriginalEcpController = PartialResourceDescriptor->u.Port.Start;
  840. Fdx->PnpInfo.SpanOfEcpController = PartialResourceDescriptor->u.Port.Length;
  841. Fdx->PnpInfo.EcpController = (PUCHAR)(ULONG_PTR)Fdx->PnpInfo.OriginalEcpController.QuadPart;
  842. Fdx->EcpAddressSpace = PartialResourceDescriptor->Flags;
  843. break;
  844. default:
  845. // don't know what this is - ignore it
  846. ;
  847. }
  848. break;
  849. case CmResourceTypeBusNumber:
  850. Fdx->BusNumber = PartialResourceDescriptor->u.BusNumber.Start;
  851. break;
  852. case CmResourceTypeInterrupt:
  853. *FoundIrq = TRUE;
  854. Fdx->FoundInterrupt = TRUE;
  855. Fdx->InterruptLevel = (KIRQL)PartialResourceDescriptor->u.Interrupt.Level;
  856. Fdx->InterruptVector = PartialResourceDescriptor->u.Interrupt.Vector;
  857. Fdx->InterruptAffinity = PartialResourceDescriptor->u.Interrupt.Affinity;
  858. if (PartialResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
  859. Fdx->InterruptMode = Latched;
  860. } else {
  861. Fdx->InterruptMode = LevelSensitive;
  862. }
  863. break;
  864. case CmResourceTypeDma:
  865. // we don't do anything with DMA - fall through to default case
  866. default:
  867. break;
  868. } // end switch( PartialResourceDescriptor->Type )
  869. } // end for(... ; i < PartialResourceList->Count ; ...)
  870. } // end if( FullResourceDescriptor )
  871. return status;
  872. }
  873. BOOLEAN PptIsPci(
  874. PFDO_EXTENSION Fdx,
  875. PIRP Irp
  876. )
  877. /*++
  878. Does this look like a PCI card? Return TRUE if yes, FALSE otherwise
  879. --*/
  880. {
  881. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
  882. PCM_RESOURCE_LIST ResourceList;
  883. PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
  884. PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
  885. PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor;
  886. ULONG i;
  887. ULONG portResourceDescriptorCount = 0;
  888. BOOLEAN largePortRangeFound = FALSE;
  889. ULONG rangeLength;
  890. //
  891. // If there are more than 2 IO resource descriptors, or if any IO resource
  892. // descriptor has a range > 8 bytes, then assume that this is a PCI device
  893. // and requires non-traditional handling.
  894. //
  895. ResourceList = irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
  896. if (ResourceList == NULL) {
  897. // we weren't given any resources
  898. return FALSE;
  899. }
  900. FullResourceDescriptor = &ResourceList->List[0];
  901. if (FullResourceDescriptor) {
  902. PartialResourceList = &FullResourceDescriptor->PartialResourceList;
  903. for (i = 0; i < PartialResourceList->Count; i++) {
  904. PartialResourceDescriptor = &PartialResourceList->PartialDescriptors[i];
  905. switch (PartialResourceDescriptor->Type) {
  906. case CmResourceTypePort:
  907. rangeLength = PartialResourceDescriptor->u.Port.Length;
  908. DD((PCE)Fdx,DDT,"pnp::PptIsPCI - CmResourceTypePort - Start= %I64x, Length= %x , \n",
  909. PartialResourceDescriptor->u.Port.Start.QuadPart, rangeLength);
  910. ++portResourceDescriptorCount;
  911. if( rangeLength > 8 ) {
  912. largePortRangeFound = TRUE;
  913. }
  914. break;
  915. default:
  916. ;
  917. } // end switch( PartialResourceDescriptor->Type )
  918. } // end for(... ; i < PartialResourceList->Count ; ...)
  919. } // end if( FullResourceDescriptor )
  920. if( (portResourceDescriptorCount > 2) || (TRUE == largePortRangeFound) ) {
  921. // looks like PCI
  922. return TRUE;
  923. } else {
  924. // does not look like PCI
  925. return FALSE;
  926. }
  927. }
  928. NTSTATUS
  929. PptPnpStartScanCmResourceList(
  930. IN PFDO_EXTENSION Fdx,
  931. IN PIRP Irp,
  932. OUT PBOOLEAN FoundPort,
  933. OUT PBOOLEAN FoundIrq,
  934. OUT PBOOLEAN FoundDma
  935. )
  936. /*++dvdf3
  937. Routine Description:
  938. This function is a helper function called by PptPnpStartDevice().
  939. This function scans the CM_RESOURCE_LIST supplied with the Pnp
  940. IRP_MN_START_DEVICE IRP, extracts the resources from the list,
  941. and saves them in the device Fdx.
  942. Arguments:
  943. Fdx - The device extension of the target of the START IRP
  944. Irp - The IRP
  945. FoundPort - Did we find a Port resource?
  946. FoundIrq - Did we find an IRQ resource?
  947. FoundDma - Did we find a DMA resource?
  948. Return Value:
  949. STATUS_SUCCESS - if we were given a resource list,
  950. STATUS_INSUFFICIENT_RESOURCES - otherwise
  951. --*/
  952. {
  953. NTSTATUS status = STATUS_SUCCESS;
  954. PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
  955. PCM_RESOURCE_LIST ResourceList;
  956. PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
  957. PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
  958. PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor;
  959. ULONG i;
  960. PHYSICAL_ADDRESS start;
  961. ULONG length;
  962. BOOLEAN isPci = FALSE;
  963. *FoundPort = FALSE;
  964. *FoundIrq = FALSE;
  965. *FoundDma = FALSE;
  966. ResourceList = irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
  967. if (ResourceList == NULL) {
  968. // we weren't given any resources, bail out
  969. DD((PCE)Fdx,DDT,"START - FAIL - No Resources - AllocatedResourcesTranslated == NULL\n");
  970. status = STATUS_INSUFFICIENT_RESOURCES;
  971. goto targetExit;
  972. }
  973. if( TRUE == PptIsPci( Fdx, Irp ) ) {
  974. // This appears to be a PCI card
  975. status = PptPnpStartScanPciCardCmResourceList(Fdx, Irp, FoundPort, FoundIrq, FoundDma);
  976. isPci=TRUE;
  977. goto targetExit;
  978. }
  979. //
  980. // Device appears to be traditional / non-PCI card parallel port
  981. //
  982. FullResourceDescriptor = &ResourceList->List[0];
  983. if (FullResourceDescriptor) {
  984. Fdx->InterfaceType = FullResourceDescriptor->InterfaceType;
  985. PartialResourceList = &FullResourceDescriptor->PartialResourceList;
  986. for (i = 0; i < PartialResourceList->Count; i++) {
  987. PartialResourceDescriptor = &PartialResourceList->PartialDescriptors[i];
  988. switch (PartialResourceDescriptor->Type) {
  989. case CmResourceTypePort:
  990. start = PartialResourceDescriptor->u.Port.Start;
  991. length = PartialResourceDescriptor->u.Port.Length;
  992. DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - start= %I64x , length=%x\n",start, length);
  993. *FoundPort = TRUE;
  994. if ((Fdx->PortInfo.OriginalController.LowPart == 0) &&
  995. (Fdx->PortInfo.OriginalController.HighPart == 0)) {
  996. DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - assuming Controller\n");
  997. Fdx->PortInfo.OriginalController = PartialResourceDescriptor->u.Port.Start;
  998. Fdx->PortInfo.SpanOfController = PartialResourceDescriptor->u.Port.Length;
  999. Fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)Fdx->PortInfo.OriginalController.QuadPart;
  1000. Fdx->AddressSpace = PartialResourceDescriptor->Flags;
  1001. } else if ((Fdx->PnpInfo.OriginalEcpController.LowPart == 0) &&
  1002. (Fdx->PnpInfo.OriginalEcpController.HighPart == 0) &&
  1003. (IsNotNEC_98)) {
  1004. if ((PartialResourceDescriptor->u.Port.Start.LowPart < Fdx->PortInfo.OriginalController.LowPart) &&
  1005. (PartialResourceDescriptor->u.Port.Start.HighPart < Fdx->PortInfo.OriginalController.HighPart)) {
  1006. //
  1007. // Swapping address spaces
  1008. //
  1009. DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - assuming Controller - Swapping Controller/EcpController\n");
  1010. Fdx->PnpInfo.OriginalEcpController = Fdx->PortInfo.OriginalController;
  1011. Fdx->PnpInfo.SpanOfEcpController = Fdx->PortInfo.SpanOfController;
  1012. Fdx->PnpInfo.EcpController = Fdx->PortInfo.Controller;
  1013. Fdx->EcpAddressSpace = Fdx->AddressSpace;
  1014. Fdx->PortInfo.OriginalController = PartialResourceDescriptor->u.Port.Start;
  1015. Fdx->PortInfo.SpanOfController = PartialResourceDescriptor->u.Port.Length;
  1016. Fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)Fdx->PortInfo.OriginalController.QuadPart;
  1017. Fdx->AddressSpace = PartialResourceDescriptor->Flags;
  1018. } else {
  1019. DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - assuming EcpController\n");
  1020. Fdx->PnpInfo.OriginalEcpController = PartialResourceDescriptor->u.Port.Start;
  1021. Fdx->PnpInfo.SpanOfEcpController = PartialResourceDescriptor->u.Port.Length;
  1022. Fdx->PnpInfo.EcpController = (PUCHAR)(ULONG_PTR)Fdx->PnpInfo.OriginalEcpController.QuadPart;
  1023. Fdx->EcpAddressSpace = PartialResourceDescriptor->Flags;
  1024. }
  1025. }
  1026. break;
  1027. case CmResourceTypeBusNumber:
  1028. Fdx->BusNumber = PartialResourceDescriptor->u.BusNumber.Start;
  1029. break;
  1030. case CmResourceTypeInterrupt:
  1031. *FoundIrq = TRUE;
  1032. Fdx->FoundInterrupt = TRUE;
  1033. Fdx->InterruptLevel = (KIRQL)PartialResourceDescriptor->u.Interrupt.Level;
  1034. Fdx->InterruptVector = PartialResourceDescriptor->u.Interrupt.Vector;
  1035. Fdx->InterruptAffinity = PartialResourceDescriptor->u.Interrupt.Affinity;
  1036. if (PartialResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
  1037. Fdx->InterruptMode = Latched;
  1038. } else {
  1039. Fdx->InterruptMode = LevelSensitive;
  1040. }
  1041. break;
  1042. case CmResourceTypeDma:
  1043. // we don't do anything with DMA - fall through to default case
  1044. default:
  1045. break;
  1046. } // end switch( PartialResourceDescriptor->Type )
  1047. } // end for(... ; i < PartialResourceList->Count ; ...)
  1048. } // end if( FullResourceDescriptor )
  1049. targetExit:
  1050. if( FALSE == isPci ) {
  1051. // we scanned the resources - dump what we found
  1052. DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - done, found:\n");
  1053. DD((PCE)Fdx,DDT," OriginalEcpController= %I64x\n", Fdx->PnpInfo.OriginalEcpController);
  1054. DD((PCE)Fdx,DDT," EcpController = %p\n", Fdx->PnpInfo.EcpController);
  1055. DD((PCE)Fdx,DDT," SpanOfEcpController = %x\n", Fdx->PnpInfo.SpanOfEcpController);
  1056. }
  1057. return status;
  1058. }
  1059. NTSTATUS
  1060. PptPnpStartValidateResources(
  1061. IN PDEVICE_OBJECT DeviceObject,
  1062. IN BOOLEAN FoundPort,
  1063. IN BOOLEAN FoundIrq,
  1064. IN BOOLEAN FoundDma
  1065. )
  1066. /*++dvdf3
  1067. Routine Description:
  1068. This function is a helper function called by PptPnpStartDevice().
  1069. This function does a sanity check of the resources saved in our
  1070. extension by PptPnpStartScanCmResourceList() to determine
  1071. if those resources appear to be valid. Checks for for Irq
  1072. and Dma resource validity are anticipated in a future version.
  1073. Arguments:
  1074. DeviceObject - The target of the START IRP
  1075. FoundPort - Did we find a Port resource?
  1076. FoundIrq - Did we find an IRQ resource?
  1077. FoundDma - Did we find a DMA resource?
  1078. Return Value:
  1079. STATUS_SUCCESS - on success,
  1080. STATUS_NO_SUCH_DEVICE - if we weren't given a port resource,
  1081. STATUS_NONE_MAPPED - if we were given a port resource but our
  1082. port address is NULL
  1083. --*/
  1084. {
  1085. PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
  1086. NTSTATUS status = STATUS_SUCCESS;
  1087. UNREFERENCED_PARAMETER( FoundIrq ); // future use
  1088. UNREFERENCED_PARAMETER( FoundDma ); // future use
  1089. if( !FoundPort ) {
  1090. status = STATUS_NO_SUCH_DEVICE;
  1091. } else {
  1092. // fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)fdx->PortInfo.OriginalController.LowPart;
  1093. fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)fdx->PortInfo.OriginalController.QuadPart;
  1094. if(!fdx->PortInfo.Controller) {
  1095. // ( Controller == NULL ) is invalid
  1096. PptLogError(DeviceObject->DriverObject, DeviceObject,
  1097. fdx->PortInfo.OriginalController, PhysicalZero, 0, 0, 0, 10,
  1098. STATUS_SUCCESS, PAR_REGISTERS_NOT_MAPPED);
  1099. status = STATUS_NONE_MAPPED;
  1100. }
  1101. }
  1102. return status;
  1103. }
  1104. BOOLEAN
  1105. PptPnpFilterExistsNonIrqResourceList(
  1106. IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
  1107. )
  1108. /*++dvdf8
  1109. Routine Description:
  1110. This function is a helper function called by
  1111. PptPnpFilterResourceRequirements().
  1112. This function scans the IO_RESOURCE_REQUIREMENTS_LIST to determine
  1113. whether there exists any resource alternatives that do NOT contain
  1114. an IRQ resource descriptor. The method used to filter out IRQ
  1115. resources may differ based on whether or not there exists a
  1116. resource alternative that does not contain an IRQ resource
  1117. descriptor.
  1118. Arguments:
  1119. ResourceRequirementsList - The list to scan.
  1120. Return Value:
  1121. TRUE - There exists at least one resource alternative in the list that
  1122. does not contain an IRQ resource descriptor.
  1123. FALSE - Otherwise.
  1124. --*/
  1125. {
  1126. ULONG listCount = ResourceRequirementsList->AlternativeLists;
  1127. PIO_RESOURCE_LIST curList;
  1128. ULONG i;
  1129. i=0;
  1130. curList = ResourceRequirementsList->List;
  1131. while( i < listCount ) {
  1132. DD(NULL,DDT,"Searching List i=%d for an IRQ, curList= %x\n", i,curList);
  1133. {
  1134. ULONG remain = curList->Count;
  1135. PIO_RESOURCE_DESCRIPTOR curDesc = curList->Descriptors;
  1136. BOOLEAN foundIrq = FALSE;
  1137. while( remain ) {
  1138. DD(NULL,DDT," curDesc= %x , remain=%d\n", curDesc, remain);
  1139. if(curDesc->Type == CmResourceTypeInterrupt) {
  1140. DD(NULL,DDT," Found IRQ - skip to next list\n");
  1141. foundIrq = TRUE;
  1142. break;
  1143. }
  1144. ++curDesc;
  1145. --remain;
  1146. }
  1147. if( foundIrq == FALSE ) {
  1148. //
  1149. // We found a resource list that does not contain an IRQ resource.
  1150. // Our search is over.
  1151. //
  1152. DD(NULL,DDT," Found a list with NO IRQ - return TRUE from PptPnpFilterExistsNonIrqResourceList\n");
  1153. return TRUE;
  1154. }
  1155. }
  1156. //
  1157. // The next list starts immediately after the last descriptor of the current list.
  1158. //
  1159. curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
  1160. ++i;
  1161. }
  1162. //
  1163. // All resource alternatives contain at least one IRQ resource descriptor.
  1164. //
  1165. DD(NULL,DDT,"all lists contain IRQs - return FALSE from PptPnpFilterExistsNonIrqResourceList\n");
  1166. return FALSE;
  1167. }
  1168. VOID
  1169. PptPnpFilterRemoveIrqResourceLists(
  1170. PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
  1171. )
  1172. /*++dvdf8
  1173. Routine Description:
  1174. This function is a helper function called by
  1175. PptPnpFilterResourceRequirements().
  1176. This function removes all resource alternatives (IO_RESOURCE_LISTs)
  1177. that contain IRQ resources from the IO_RESOURCE_REQUIREMENTS_LIST
  1178. Arguments:
  1179. ResourceRequirementsList - The list to process.
  1180. Return Value:
  1181. none.
  1182. --*/
  1183. {
  1184. ULONG listCount = ResourceRequirementsList->AlternativeLists;
  1185. PIO_RESOURCE_LIST curList;
  1186. PIO_RESOURCE_LIST nextList;
  1187. ULONG i;
  1188. PCHAR currentEndOfResourceRequirementsList;
  1189. LONG bytesToMove;
  1190. DD(NULL,DDT,"Enter PptPnpFilterRemoveIrqResourceLists() - AlternativeLists= %d\n", listCount);
  1191. //
  1192. // We use the end of the list to compute the size of the memory
  1193. // block to move when we remove a resource alternative from the
  1194. // list of lists.
  1195. //
  1196. currentEndOfResourceRequirementsList = PptPnpFilterGetEndOfResourceRequirementsList(ResourceRequirementsList);
  1197. i=0;
  1198. curList = ResourceRequirementsList->List;
  1199. //
  1200. // Walk through the IO_RESOURCE_LISTs.
  1201. //
  1202. while( i < listCount ) {
  1203. if( PptPnpListContainsIrqResourceDescriptor(curList) ) {
  1204. //
  1205. // The current list contains IRQ, remove it by shifting the
  1206. // remaining lists into its place and decrementing the list count.
  1207. //
  1208. DD(NULL,DDT,"list contains an IRQ - Removing List\n");
  1209. //
  1210. // Get a pointer to the start of the next list.
  1211. //
  1212. nextList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
  1213. //
  1214. // compute the number of bytes to move
  1215. //
  1216. bytesToMove = (LONG)(currentEndOfResourceRequirementsList - (PCHAR)nextList);
  1217. //
  1218. // if (currentEndOfResourceRequirementsList == next list),
  1219. // then this is the last list so there is nothing to move.
  1220. //
  1221. if( bytesToMove > 0 ) {
  1222. //
  1223. // More lists remain - shift them into the hole.
  1224. //
  1225. RtlMoveMemory(curList, nextList, bytesToMove);
  1226. //
  1227. // Adjust the pointer to the end of of the
  1228. // IO_RESOURCE_REQUIREMENTS_LIST (list of lists) due to the shift.
  1229. //
  1230. currentEndOfResourceRequirementsList -= ( (PCHAR)nextList - (PCHAR)curList );
  1231. }
  1232. //
  1233. // Note that we removed an IO_RESOURCE_LIST from the IO_RESOURCE_REQUIREMENTS_LIST.
  1234. //
  1235. --listCount;
  1236. } else {
  1237. //
  1238. // The current list does not contain an IRQ resource, advance to next list.
  1239. //
  1240. DD(NULL,DDT,"list does not contain an IRQ - i=%d listCount=%d curList= %#x\n", i,listCount,curList);
  1241. curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
  1242. ++i;
  1243. }
  1244. }
  1245. //
  1246. // Note the post filtered list count in the ResourceRequirementsList.
  1247. //
  1248. ResourceRequirementsList->AlternativeLists = listCount;
  1249. DD(NULL,DDT,"Leave PptPnpFilterRemoveIrqResourceLists() - AlternativeLists= %d\n", listCount);
  1250. return;
  1251. }
  1252. PVOID
  1253. PptPnpFilterGetEndOfResourceRequirementsList(
  1254. IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
  1255. )
  1256. /*++dvdf8
  1257. Routine Description:
  1258. This function is a helper function called by PptPnpFilterRemoveIrqResourceLists()
  1259. This function finds the end of an IO_RESOURCE_REQUIREMENTS_LIST
  1260. (list of IO_RESOURCE_LISTs).
  1261. Arguments:
  1262. ResourceRequirementsList - The list to scan.
  1263. Return Value:
  1264. Pointer to the next address past the end of the IO_RESOURCE_REQUIREMENTS_LIST.
  1265. --*/
  1266. {
  1267. ULONG listCount = ResourceRequirementsList->AlternativeLists;
  1268. PIO_RESOURCE_LIST curList;
  1269. ULONG i;
  1270. i=0;
  1271. curList = ResourceRequirementsList->List;
  1272. while( i < listCount ) {
  1273. //
  1274. // Pointer arithmetic based on the size of an IO_RESOURCE_DESCRIPTOR.
  1275. //
  1276. curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
  1277. ++i;
  1278. }
  1279. return (PVOID)curList;
  1280. }
  1281. VOID
  1282. PptPnpFilterNukeIrqResourceDescriptorsFromAllLists(
  1283. PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
  1284. )
  1285. /*++dvdf8
  1286. Routine Description:
  1287. This function is a helper function called by
  1288. PptPnpFilterResourceRequirements().
  1289. This function "nukes" all IRQ resources descriptors
  1290. in the IO_RESOURCE_REQUIREMENTS_LIST by changing the descriptor
  1291. types from CmResourceTypeInterrupt to CmResourceTypeNull.
  1292. Arguments:
  1293. ResourceRequirementsList - The list to process.
  1294. Return Value:
  1295. none.
  1296. --*/
  1297. {
  1298. ULONG listCount = ResourceRequirementsList->AlternativeLists;
  1299. ULONG i = 0;
  1300. PIO_RESOURCE_LIST curList = ResourceRequirementsList->List;
  1301. DD(NULL,DDT,"Enter PptPnpFilterNukeIrqResourceDescriptorsFromAllLists() - AlternativeLists= %d\n", listCount);
  1302. //
  1303. // Walk through the list of IO_RESOURCE_LISTs in the IO_RESOURCE_REQUIREMENTS list.
  1304. //
  1305. while( i < listCount ) {
  1306. DD(NULL,DDT,"Nuking IRQs from List i=%d, curList= %x\n", i,curList);
  1307. //
  1308. // Nuke all IRQ resources from the current IO_RESOURCE_LIST.
  1309. //
  1310. PptPnpFilterNukeIrqResourceDescriptors( curList );
  1311. curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
  1312. ++i;
  1313. }
  1314. }
  1315. VOID
  1316. PptPnpFilterNukeIrqResourceDescriptors(
  1317. PIO_RESOURCE_LIST IoResourceList
  1318. )
  1319. /*++dvdf8
  1320. Routine Description:
  1321. This function is a helper function called by
  1322. PptPnpFilterNukeIrqResourceDescriptorsFromAllLists().
  1323. This function "nukes" all IRQ resources descriptors
  1324. in the IO_RESOURCE_LIST by changing the descriptor
  1325. types from CmResourceTypeInterrupt to CmResourceTypeNull.
  1326. Arguments:
  1327. IoResourceList - The list to process.
  1328. Return Value:
  1329. none.
  1330. --*/
  1331. {
  1332. PIO_RESOURCE_DESCRIPTOR pIoResourceDescriptorIn = IoResourceList->Descriptors;
  1333. ULONG i;
  1334. //
  1335. // Scan the descriptor list for Interrupt descriptors.
  1336. //
  1337. for (i = 0; i < IoResourceList->Count; ++i) {
  1338. if (pIoResourceDescriptorIn->Type == CmResourceTypeInterrupt) {
  1339. //
  1340. // Found one - change resource type from Interrupt to Null.
  1341. //
  1342. pIoResourceDescriptorIn->Type = CmResourceTypeNull;
  1343. DD(NULL,DDT," - giving up IRQ resource - MinimumVector: %d MaximumVector: %d\n",
  1344. pIoResourceDescriptorIn->u.Interrupt.MinimumVector,
  1345. pIoResourceDescriptorIn->u.Interrupt.MaximumVector);
  1346. }
  1347. ++pIoResourceDescriptorIn;
  1348. }
  1349. }
  1350. BOOLEAN
  1351. PptPnpListContainsIrqResourceDescriptor(
  1352. IN PIO_RESOURCE_LIST List
  1353. )
  1354. {
  1355. ULONG i;
  1356. PIO_RESOURCE_DESCRIPTOR curDesc = List->Descriptors;
  1357. for(i=0; i<List->Count; ++i) {
  1358. if(curDesc->Type == CmResourceTypeInterrupt) {
  1359. return TRUE;
  1360. } else {
  1361. ++curDesc;
  1362. }
  1363. }
  1364. return FALSE;
  1365. }
  1366. NTSTATUS
  1367. PptPnpBounceAndCatchPnpIrp(
  1368. PFDO_EXTENSION Fdx,
  1369. PIRP Irp
  1370. )
  1371. /*++
  1372. Pass a PnP IRP down the stack to our parent and catch it on the way back
  1373. up after it has been handled by the drivers below us in the driver stack.
  1374. --*/
  1375. {
  1376. NTSTATUS status;
  1377. KEVENT event;
  1378. PDEVICE_OBJECT parentDevObj = Fdx->ParentDeviceObject;
  1379. DD((PCE)Fdx,DDT,"PptBounceAndCatchPnpIrp()\n");
  1380. // setup
  1381. KeInitializeEvent(&event, NotificationEvent, FALSE);
  1382. IoCopyCurrentIrpStackLocationToNext(Irp);
  1383. IoSetCompletionRoutine(Irp, PptSynchCompletionRoutine, &event, TRUE, TRUE, TRUE);
  1384. // send
  1385. status = IoCallDriver(parentDevObj, Irp);
  1386. // wait for completion routine to signal that it has caught the IRP on
  1387. // its way back out
  1388. KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
  1389. if (status == STATUS_PENDING) {
  1390. // If IoCallDriver returned STATUS_PENDING, then we must
  1391. // extract the "real" status from the IRP
  1392. status = Irp->IoStatus.Status;
  1393. }
  1394. return status;
  1395. }
  1396. NTSTATUS
  1397. PptPnpPassThroughPnpIrpAndReleaseRemoveLock(
  1398. IN PFDO_EXTENSION Fdx,
  1399. IN PIRP Irp
  1400. )
  1401. /*++
  1402. Pass a PnP IRP down the stack to our parent,
  1403. release RemoveLock, and return status from IoCallDriver.
  1404. --*/
  1405. {
  1406. NTSTATUS status;
  1407. IoSkipCurrentIrpStackLocation(Irp);
  1408. status = IoCallDriver(Fdx->ParentDeviceObject, Irp);
  1409. PptReleaseRemoveLock(&Fdx->RemoveLock, Irp);
  1410. return status;
  1411. }
  1412. VOID
  1413. P4DestroyPdo(
  1414. IN PDEVICE_OBJECT Pdo
  1415. )
  1416. {
  1417. PPDO_EXTENSION pdx = Pdo->DeviceExtension;
  1418. PDEVICE_OBJECT fdo = pdx->Fdo;
  1419. PFDO_EXTENSION fdx = fdo->DeviceExtension;
  1420. DD((PCE)pdx,DDT,"P4DestroyPdo\n");
  1421. //
  1422. // Remove registry entry under HKLM\HARDWARE\DEVICEMAP\PARALLEL PORTS
  1423. //
  1424. if( pdx->PdoName ) {
  1425. NTSTATUS status = RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP, (PWSTR)L"PARALLEL PORTS", pdx->PdoName );
  1426. if( status != STATUS_SUCCESS ) {
  1427. DD((PCE)pdx,DDW,"P4DestroyPdo - Failed to Delete DEVICEMAP registry entry - status=%x\n",status);
  1428. }
  1429. }
  1430. //
  1431. // remove self from FDO's DevDeletionListHead list
  1432. //
  1433. if( !IsListEmpty( &fdx->DevDeletionListHead ) ) {
  1434. BOOLEAN done = FALSE;
  1435. PLIST_ENTRY first = NULL;
  1436. while( !done ) {
  1437. // look for self on list - remove if found
  1438. PLIST_ENTRY current = RemoveHeadList( &fdx->DevDeletionListHead );
  1439. if( CONTAINING_RECORD( current, PDO_EXTENSION, DevDeletionList ) != pdx ) {
  1440. // this is not the entry that we are looking for
  1441. if( !first ) {
  1442. // note the first entry so we can stop if we search the entire list and don't find self
  1443. first = current;
  1444. InsertTailList( &fdx->DevDeletionListHead, current );
  1445. } else {
  1446. // have we searched the entire list?
  1447. if( first == current ) {
  1448. // we searched the entire list and didn't find self - we must not be on the list
  1449. // put entry back on front of list, then we're done with search
  1450. DD((PCE)pdx,DDT,"P4DestroyPdo - searched entire list - we're not on it - done with search\n");
  1451. InsertHeadList( &fdx->DevDeletionListHead, current );
  1452. done = TRUE;
  1453. } else {
  1454. // not the entry that we're looking for - place at end of list - continue search
  1455. InsertTailList( &fdx->DevDeletionListHead, current );
  1456. }
  1457. }
  1458. } else {
  1459. // found self - self removed from list - done with search
  1460. DD((PCE)pdx,DDT,"P4DestroyPdo - found self on FDO's DevDeletionListHead and removed self - done with search\n");
  1461. done = TRUE;
  1462. }
  1463. } // end while( !done )
  1464. } // endif( !IsListEmpty... )
  1465. //
  1466. // clean up any ShadowBuffer queue used by hardware ECP modes
  1467. //
  1468. if( pdx->bShadowBuffer ) {
  1469. BOOLEAN queueDeleted = Queue_Delete( &(pdx->ShadowBuffer) );
  1470. if( !queueDeleted ) {
  1471. PptAssertMsg( "Failed to delete queue?!?", FALSE );
  1472. }
  1473. pdx->bShadowBuffer = FALSE;
  1474. }
  1475. PptAssert( NULL == pdx->ShadowBuffer.theArray );
  1476. //
  1477. // clean up symbolic link - unless it has been previously cleaned up elsewhere
  1478. //
  1479. if( pdx->SymLinkName ) {
  1480. P5DeletePdoSymLink( Pdo );
  1481. }
  1482. //
  1483. // clean up other device extension pool allocations
  1484. //
  1485. if( pdx->Mfg ) {
  1486. DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Mfg <%s>\n", pdx->Mfg);
  1487. ExFreePool( pdx->Mfg );
  1488. pdx->Mfg = NULL;
  1489. }
  1490. if( pdx->Mdl ) {
  1491. DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Mdl <%s>\n", pdx->Mdl);
  1492. ExFreePool( pdx->Mdl );
  1493. pdx->Mdl = NULL;
  1494. }
  1495. if( pdx->Cid ) {
  1496. DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Cid <%s>\n", pdx->Cid);
  1497. ExFreePool( pdx->Cid );
  1498. pdx->Cid = NULL;
  1499. }
  1500. if( pdx->DeviceInterface.Buffer ) {
  1501. DD((PCE)pdx,DDT,"P4DestroyPdo - clean up DeviceInterface <%S>\n", pdx->PdoName);
  1502. RtlFreeUnicodeString( &pdx->DeviceInterface );
  1503. pdx->DeviceInterfaceState = FALSE;
  1504. }
  1505. if( pdx->PdoName ) {
  1506. DD((PCE)pdx,DDT,"P4DestroyPdo - clean up PdoName <%S>\n", pdx->PdoName);
  1507. ExFreePool( pdx->PdoName );
  1508. pdx->PdoName = NULL;
  1509. }
  1510. if( pdx->Location ) {
  1511. DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Location <%s>\n", pdx->Location);
  1512. ExFreePool( pdx->Location );
  1513. pdx->Location = NULL;
  1514. }
  1515. //
  1516. // delete device object
  1517. //
  1518. IoDeleteDevice( Pdo );
  1519. }
  1520. VOID
  1521. P4SanitizeId(
  1522. IN OUT PWSTR DeviceId
  1523. )
  1524. /*++
  1525. Routine Description:
  1526. This routine parses the UNICODE_NULL terminated string and replaces any invalid
  1527. characters with an underscore character.
  1528. Invalid characters are:
  1529. c <= 0x20 (L' ')
  1530. c > 0x7F
  1531. c == 0x2C (L',')
  1532. Arguments:
  1533. DeviceId - specifies a device id string (or part of one), must be
  1534. UNICODE_NULL terminated.
  1535. Return Value:
  1536. None.
  1537. --*/
  1538. {
  1539. PWCHAR p;
  1540. for( p = DeviceId; *p; ++p ) {
  1541. if( (*p <= L' ') || (*p > (WCHAR)0x7F) || (*p == L',') ) {
  1542. *p = L'_';
  1543. }
  1544. }
  1545. }
  1546. NTSTATUS
  1547. P4InitializePdo(
  1548. IN PDEVICE_OBJECT Fdo,
  1549. IN PDEVICE_OBJECT Pdo,
  1550. IN enum _PdoType PdoType,
  1551. IN UCHAR DaisyChainId, // Ignored unless PdoTypeDaisyChain == PdoType
  1552. IN PCHAR Ieee1284Id, // NULL if none
  1553. IN PWSTR PdoName,
  1554. IN PWSTR SymLinkName
  1555. )
  1556. {
  1557. PFDO_EXTENSION fdx = Fdo->DeviceExtension;
  1558. PPDO_EXTENSION pdx = Pdo->DeviceExtension;
  1559. // we do buffered IO rather than direct IO
  1560. Pdo->Flags |= DO_BUFFERED_IO;
  1561. // DO_POWER_PAGABLE should be set same as parent FDO
  1562. Pdo->Flags |= ( Fdo->Flags & DO_POWER_PAGABLE );
  1563. // need to be able to forward Irps to parent
  1564. Pdo->StackSize = Fdo->StackSize + 1;
  1565. RtlZeroMemory( pdx, sizeof(PDO_EXTENSION) );
  1566. // used by debugger extension
  1567. pdx->Signature1 = PARPORT_TAG;
  1568. pdx->Signature2 = PARPORT_TAG;
  1569. // frequently need to know what type of PDO we have in order to do special case handling
  1570. pdx->PdoType = PdoType;
  1571. // Save name used in call to IoCreateDevice (for debugging use)
  1572. pdx->PdoName = PdoName;
  1573. // Save name used in call to IoCreateUnprotectedSymbolicLink for later call to IoDeleteSymbolicLink
  1574. pdx->SymLinkName = SymLinkName;
  1575. // initialize Mfg, Mdl, and Cid
  1576. if( Ieee1284Id ) {
  1577. //
  1578. // Extract Mfg, Mdl, and Cid from Ieee1284Id and save in extension
  1579. //
  1580. // ParPnpFindDeviceIdKeys modifies deviceID passed in so make
  1581. // a copy of the 1284 ID and pass in a pointer to the copy
  1582. PCHAR tmpBuffer;
  1583. ULONG tmpBufLen = strlen(Ieee1284Id) + sizeof(CHAR);
  1584. DD((PCE)fdx,DDT,"P4InitializePdo - have Ieee1284Id\n");
  1585. tmpBuffer = ExAllocatePool( PagedPool, tmpBufLen );
  1586. if( tmpBuffer ) {
  1587. PCHAR mfg, mdl, cls, des, aid, cid;
  1588. RtlZeroMemory( tmpBuffer, tmpBufLen );
  1589. strcpy( tmpBuffer, Ieee1284Id );
  1590. DD((PCE)fdx,DDT,"P4InitializePdo - calling ParPnpFindDeviceIdKeys\n");
  1591. ParPnpFindDeviceIdKeys( &mfg, &mdl, &cls, &des, &aid, &cid, tmpBuffer );
  1592. if( mfg ) {
  1593. PCHAR buffer;
  1594. ULONG bufLen = strlen(mfg) + sizeof(CHAR);
  1595. DD((PCE)fdx,DDT,"P4InitializePdo - found mfg - <%s>\n",mfg);
  1596. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
  1597. if( buffer ) {
  1598. RtlZeroMemory( buffer, bufLen );
  1599. strcpy( buffer, mfg );
  1600. pdx->Mfg = buffer;
  1601. }
  1602. }
  1603. if( mdl ) {
  1604. PCHAR buffer;
  1605. ULONG bufLen = strlen(mdl) + sizeof(CHAR);
  1606. DD((PCE)fdx,DDT,"P4InitializePdo - found mdl - <%s>\n",mdl);
  1607. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
  1608. if( buffer ) {
  1609. RtlZeroMemory( buffer, bufLen );
  1610. strcpy( buffer, mdl );
  1611. pdx->Mdl = buffer;
  1612. }
  1613. }
  1614. if( cid ) {
  1615. PCHAR buffer;
  1616. ULONG bufLen = strlen(cid) + sizeof(CHAR);
  1617. DD((PCE)fdx,DDT,"P4InitializePdo - found cid - <%s>\n",cid);
  1618. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
  1619. if( buffer ) {
  1620. RtlZeroMemory( buffer, bufLen );
  1621. strcpy( buffer, cid );
  1622. pdx->Cid = buffer;
  1623. }
  1624. } else {
  1625. DD((PCE)fdx,DDT,"P4InitializePdo - no cid found\n");
  1626. }
  1627. ExFreePool( tmpBuffer );
  1628. } else {
  1629. DD((PCE)fdx,DDT,"P4InitializePdo - out of pool\n");
  1630. }
  1631. } else {
  1632. //
  1633. // PdoType doesn't have a Mfg, Mdl, or Cid, make up Mfg and Mdl, no Cid
  1634. //
  1635. const CHAR rawPortMfg[] = "Microsoft";
  1636. const CHAR rawPortMdl[] = "RawPort";
  1637. const CHAR legacyZipMfg[] = "IMG";
  1638. const CHAR legacyZipMdl[] = "VP0";
  1639. PCHAR mfgStr;
  1640. ULONG mfgLen;
  1641. PCHAR mdlStr;
  1642. ULONG mdlLen;
  1643. PCHAR buffer;
  1644. if( PdoTypeRawPort == PdoType ) {
  1645. mfgStr = (PCHAR)rawPortMfg;
  1646. mfgLen = sizeof(rawPortMfg);
  1647. mdlStr = (PCHAR)rawPortMdl;
  1648. mdlLen = sizeof(rawPortMdl);
  1649. } else {
  1650. // PdoTypeLegacyZip
  1651. PptAssert( PdoTypeLegacyZip == PdoType );
  1652. mfgStr = (PCHAR)legacyZipMfg;
  1653. mfgLen = sizeof(legacyZipMfg);
  1654. mdlStr = (PCHAR)legacyZipMdl;
  1655. mdlLen = sizeof(legacyZipMdl);
  1656. }
  1657. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, mfgLen );
  1658. if( buffer ) {
  1659. RtlZeroMemory( buffer, mfgLen );
  1660. strcpy( buffer, mfgStr );
  1661. pdx->Mfg = buffer;
  1662. }
  1663. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, mdlLen );
  1664. if( buffer ) {
  1665. RtlZeroMemory( buffer, mdlLen );
  1666. strcpy( buffer, mdlStr );
  1667. pdx->Mdl = buffer;
  1668. }
  1669. pdx->Cid = NULL;
  1670. }
  1671. // initialize Location information - LPTx or LPTx.y
  1672. PptAssert( fdx->PnpInfo.PortName &&
  1673. ( (0 == wcscmp(fdx->PnpInfo.PortName, L"LPT1") ) ||
  1674. (0 == wcscmp(fdx->PnpInfo.PortName, L"LPT2") ) ||
  1675. (0 == wcscmp(fdx->PnpInfo.PortName, L"LPT3") ) ) );
  1676. switch( PdoType ) {
  1677. PCHAR buffer;
  1678. ULONG bufLen;
  1679. case PdoTypeRawPort :
  1680. bufLen = sizeof("LPTx");
  1681. buffer = ExAllocatePool( NonPagedPool, bufLen );
  1682. if( buffer ) {
  1683. RtlZeroMemory( buffer, bufLen );
  1684. _snprintf( buffer, bufLen, "%S", fdx->PnpInfo.PortName );
  1685. pdx->Location = buffer;
  1686. } else {
  1687. DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
  1688. }
  1689. break;
  1690. case PdoTypeDaisyChain :
  1691. bufLen = sizeof("LPTx.y");
  1692. buffer = ExAllocatePool( NonPagedPool, bufLen );
  1693. if( buffer ) {
  1694. PptAssert( DaisyChainId >= 0 && DaisyChainId < 4 );
  1695. RtlZeroMemory( buffer, bufLen );
  1696. _snprintf( buffer, bufLen, "%S.%1d", fdx->PnpInfo.PortName, DaisyChainId );
  1697. pdx->Location = buffer;
  1698. } else {
  1699. DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
  1700. }
  1701. break;
  1702. case PdoTypeEndOfChain :
  1703. bufLen = sizeof("LPTx.y");
  1704. buffer = ExAllocatePool( NonPagedPool, bufLen );
  1705. if( buffer ) {
  1706. RtlZeroMemory( buffer, bufLen );
  1707. _snprintf( buffer, bufLen, "%S.4", fdx->PnpInfo.PortName );
  1708. pdx->Location = buffer;
  1709. } else {
  1710. DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
  1711. }
  1712. break;
  1713. case PdoTypeLegacyZip :
  1714. bufLen = sizeof("LPTx.y");
  1715. buffer = ExAllocatePool( NonPagedPool, bufLen );
  1716. if( buffer ) {
  1717. RtlZeroMemory( buffer, bufLen );
  1718. _snprintf( buffer, bufLen, "%S.5", fdx->PnpInfo.PortName );
  1719. pdx->Location = buffer;
  1720. } else {
  1721. DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
  1722. }
  1723. break;
  1724. default :
  1725. PptAssert(!"Invalid PdoType");
  1726. }
  1727. // initialize synchronization and list mechanisms
  1728. ExInitializeFastMutex( &pdx->OpenCloseMutex );
  1729. InitializeListHead( &pdx->WorkQueue );
  1730. KeInitializeSemaphore( &pdx->RequestSemaphore, 0, MAXLONG );
  1731. KeInitializeEvent( &pdx->PauseEvent, NotificationEvent, TRUE );
  1732. // general info
  1733. pdx->DeviceObject = Pdo;
  1734. pdx->DevType = DevTypePdo;
  1735. pdx->EndOfChain = (PdoTypeEndOfChain == PdoType) ? TRUE : FALSE; // override later if this is a
  1736. pdx->Ieee1284_3DeviceId = (PdoTypeDaisyChain == PdoType) ? DaisyChainId : 0; // 1284.3 Daisy Chain device
  1737. pdx->IsPdo = TRUE; // really means !FDO
  1738. pdx->Fdo = Fdo;
  1739. pdx->ParClassFdo = Fdo; // depricated - use Fdo field on prev line
  1740. pdx->PortDeviceObject = Fdo; // depricated - use Fdo field 2 lines up - modify functions to use it
  1741. pdx->BusyDelay = 0;
  1742. pdx->BusyDelayDetermined = FALSE;
  1743. // timing constants
  1744. pdx->TimerStart = PAR_WRITE_TIMEOUT_VALUE;
  1745. pdx->AbsoluteOneSecond.QuadPart = 10*1000*1000;
  1746. pdx->IdleTimeout.QuadPart = - 250*10*1000; // 250 ms
  1747. pdx->OneSecond.QuadPart = - pdx->AbsoluteOneSecond.QuadPart;
  1748. // init IEEE 1284 protocol settings
  1749. ParInitializeExtension1284Info( pdx );
  1750. pdx->DeviceType = PAR_DEVTYPE_PDO; // deprecated - use DevType in common extension
  1751. if( Ieee1284Id ) {
  1752. ULONG length = strlen(Ieee1284Id) + 1;
  1753. PCHAR copyOfIeee1284Id = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, length );
  1754. if( copyOfIeee1284Id ) {
  1755. RtlZeroMemory( copyOfIeee1284Id, length );
  1756. strcpy( copyOfIeee1284Id, Ieee1284Id );
  1757. ParDetectDot3DataLink( pdx, Ieee1284Id );
  1758. ExFreePool( copyOfIeee1284Id );
  1759. }
  1760. }
  1761. // RMT - doug - need to put this back in - ParCheckParameters(DevObj->DeviceExtension); // Check the registry for parameter overrides
  1762. // Write symbolic link map info to the registry.
  1763. {
  1764. NTSTATUS status = RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP,
  1765. (PWSTR)L"PARALLEL PORTS",
  1766. pdx->PdoName,
  1767. REG_SZ,
  1768. pdx->SymLinkName,
  1769. wcslen(pdx->SymLinkName)*sizeof(WCHAR) + sizeof(WCHAR) );
  1770. if( NT_SUCCESS( status ) ) {
  1771. DD((PCE)fdx,DDT,"Created DEVICEMAP registry entry - %S -> %S\n",pdx->PdoName,pdx->SymLinkName);
  1772. } else {
  1773. DD((PCE)fdx,DDT,"Failed to create DEVICEMAP registry entry - status = %x\n", status);
  1774. }
  1775. }
  1776. Pdo->Flags &= ~DO_DEVICE_INITIALIZING; // Tell the IO system that we are ready to receive IRPs
  1777. return STATUS_SUCCESS;
  1778. }
  1779. PWSTR
  1780. P4MakePdoSymLinkName(
  1781. IN PWSTR LptName,
  1782. IN enum _PdoType PdoType,
  1783. IN UCHAR DaisyChainId, // ignored unless PdoType == PdoTypeDaisyChain
  1784. IN UCHAR RetryNumber
  1785. )
  1786. /*
  1787. Generate \DosDevices\LPTx or \DosDevices\LPTx.y PdoSymbolicLinkName from LPTx Name
  1788. In: LPTx
  1789. Out: \DosDevices\LPTx or \DosDevices\LPTx.y depending on PdoType
  1790. examples:
  1791. LPT1 PdoTypeEndOfChain -> \DosDevices\LPT1.4
  1792. LPT2 PdoTypeDaisyChain DaisyChainId==3 -> \DosDevices\LPT2.3
  1793. LPT3 PdoTypeRawPort -> \DosDevices\LPT3
  1794. returns - pointer to pool allocation containing PdoSymbolicLinkName on success (caller frees), or
  1795. - NULL on error
  1796. */
  1797. {
  1798. const UCHAR maxDaisyChainSuffix = 3;
  1799. const UCHAR endOfChainSuffix = 4;
  1800. const UCHAR legacyZipSuffix = 5;
  1801. const ULONG maxSymLinkNameLength = sizeof(L"\\DosDevices\\LPTx.y-z");
  1802. UCHAR suffix = 0;
  1803. PWSTR buffer;
  1804. if( !LptName ) {
  1805. PptAssert( !"NULL LptName" );
  1806. return NULL;
  1807. }
  1808. DD(NULL,DDT,"P4MakePdoSymLinkName - LptName = %S\n",LptName);
  1809. switch( PdoType ) {
  1810. case PdoTypeDaisyChain :
  1811. if( DaisyChainId > maxDaisyChainSuffix ) {
  1812. PptAssert( !"DaisyChainId > maxDaisyChainSuffix" );
  1813. return NULL;
  1814. }
  1815. suffix = DaisyChainId;
  1816. break;
  1817. case PdoTypeEndOfChain :
  1818. suffix = endOfChainSuffix;
  1819. break;
  1820. case PdoTypeLegacyZip :
  1821. suffix = legacyZipSuffix;
  1822. break;
  1823. case PdoTypeRawPort :
  1824. break; // no suffix
  1825. default :
  1826. PptAssert( !"Unrecognised PdoType" );
  1827. return NULL;
  1828. }
  1829. if( 0 == RetryNumber ) {
  1830. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxSymLinkNameLength );
  1831. if( buffer ) {
  1832. RtlZeroMemory( buffer, maxSymLinkNameLength );
  1833. if( PdoTypeRawPort == PdoType ) {
  1834. swprintf( buffer, L"\\DosDevices\\%s\0", LptName );
  1835. } else {
  1836. swprintf( buffer, L"\\DosDevices\\%s.%d\0", LptName, suffix );
  1837. }
  1838. }
  1839. } else if( RetryNumber <= 9 ) {
  1840. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxSymLinkNameLength );
  1841. if( buffer ) {
  1842. RtlZeroMemory( buffer, maxSymLinkNameLength );
  1843. if( PdoTypeRawPort == PdoType ) {
  1844. swprintf( buffer, L"\\DosDevices\\%s-%1d\0", LptName, RetryNumber );
  1845. } else {
  1846. swprintf( buffer, L"\\DosDevices\\%s.%d-%1d\0", LptName, suffix, RetryNumber );
  1847. }
  1848. }
  1849. } else {
  1850. buffer = NULL;
  1851. }
  1852. return buffer;
  1853. }
  1854. PWSTR
  1855. P4MakePdoDeviceName(
  1856. IN PWSTR LptName,
  1857. IN enum _PdoType PdoType,
  1858. IN UCHAR DaisyChainId, // ignored unless PdoType == PdoTypeDaisyChain
  1859. IN UCHAR RetryNumber // used if we had a name collision on IoCreateDevice
  1860. )
  1861. /*
  1862. Generate \Device\Parallely or \Device\Parallely.z PDO DeviceName from LPTx Name
  1863. In: LPTx
  1864. Out: \Device\Parallely or \Device\Parallely.z depending on PdoType
  1865. y == (x-1), optional .z suffix is based on type of Pdo
  1866. examples:
  1867. LPT1 PdoTypeEndOfChain -> \Device\Parallel0.4
  1868. LPT2 PdoTypeDaisyChain DaisyChainId==3 -> \Device\Parallel1.3
  1869. LPT3 PdoTypeRawPort -> \Device\Parallel2
  1870. returns - pointer to pool allocation containing PdoDeviceName on success (caller frees), or
  1871. - NULL on error
  1872. */
  1873. {
  1874. const UCHAR maxDaisyChainSuffix = 3;
  1875. const UCHAR endOfChainSuffix = 4;
  1876. const UCHAR legacyZipSuffix = 5;
  1877. ULONG maxDeviceNameLength;
  1878. UCHAR lptNumber;
  1879. UCHAR suffix = 0;
  1880. PWSTR buffer = NULL;
  1881. DD(NULL,DDT,"P4MakePdoDeviceName - LptName=<%S>, PdoType=%d, DaisyChainId=%d\n",LptName,PdoType,DaisyChainId);
  1882. if( !LptName ) {
  1883. PptAssert( !"NULL LptName" );
  1884. return NULL;
  1885. }
  1886. switch( PdoType ) {
  1887. case PdoTypeDaisyChain :
  1888. if( DaisyChainId > maxDaisyChainSuffix ) {
  1889. PptAssert( !"DaisyChainId > maxDaisyChainSuffix" );
  1890. return NULL;
  1891. }
  1892. suffix = DaisyChainId;
  1893. break;
  1894. case PdoTypeEndOfChain :
  1895. suffix = endOfChainSuffix;
  1896. break;
  1897. case PdoTypeLegacyZip :
  1898. suffix = legacyZipSuffix;
  1899. break;
  1900. case PdoTypeRawPort :
  1901. break; // no suffix
  1902. default :
  1903. PptAssert( !"Unrecognised PdoType" );
  1904. return NULL;
  1905. }
  1906. if ( 0 == wcscmp( (PCWSTR)L"LPT1", LptName ) ) { lptNumber = 1; }
  1907. else if( 0 == wcscmp( (PCWSTR)L"LPT2", LptName ) ) { lptNumber = 2; }
  1908. else if( 0 == wcscmp( (PCWSTR)L"LPT3", LptName ) ) { lptNumber = 3; }
  1909. else {
  1910. PptAssert( !"LptName not of the form LPTx where 1 <= x <= 3" );
  1911. return NULL;
  1912. }
  1913. DD(NULL,DDT,"P4MakePdoDeviceName - suffix=%d\n",suffix);
  1914. if( 0 == RetryNumber ) {
  1915. maxDeviceNameLength = sizeof(L"\\Device\\Parallelx.y");
  1916. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxDeviceNameLength );
  1917. if( buffer ) {
  1918. RtlZeroMemory( buffer, maxDeviceNameLength );
  1919. if( PdoTypeRawPort == PdoType ) {
  1920. swprintf( buffer, L"\\Device\\Parallel%d\0", lptNumber-1 );
  1921. } else {
  1922. swprintf( buffer, L"\\Device\\Parallel%d.%d\0", lptNumber-1, suffix );
  1923. }
  1924. }
  1925. } else {
  1926. if( RetryNumber <= 9 ) {
  1927. maxDeviceNameLength = sizeof(L"\\Device\\Parallelx.y-z");
  1928. buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxDeviceNameLength );
  1929. if( buffer ) {
  1930. RtlZeroMemory( buffer, maxDeviceNameLength );
  1931. if( PdoTypeRawPort == PdoType ) {
  1932. swprintf( buffer, L"\\Device\\Parallel%d-%1d\0", lptNumber-1, RetryNumber );
  1933. } else {
  1934. swprintf( buffer, L"\\Device\\Parallel%d.%d-%1d\0", lptNumber-1, suffix, RetryNumber );
  1935. }
  1936. }
  1937. }
  1938. }
  1939. if( buffer ) {
  1940. DD(NULL,DDT,"P4MakePdoDeviceName <%S>\n",buffer);
  1941. }
  1942. return buffer;
  1943. }
  1944. PDEVICE_OBJECT
  1945. P4CreatePdo(
  1946. IN PDEVICE_OBJECT Fdo,
  1947. IN enum _PdoType PdoType,
  1948. IN UCHAR DaisyChainId, // ignored unless PdoType == PdoTypeDaisyChain
  1949. IN PCHAR Ieee1284Id // NULL if device does not report IEEE 1284 Device ID
  1950. )
  1951. {
  1952. PFDO_EXTENSION fdx = Fdo->DeviceExtension;
  1953. PWSTR lptName = fdx->PnpInfo.PortName;
  1954. NTSTATUS status = STATUS_UNSUCCESSFUL;
  1955. PDEVICE_OBJECT pdo = NULL;
  1956. PWSTR wstrDeviceName = NULL;
  1957. PWSTR wstrSymLinkName = NULL;
  1958. BOOLEAN createdSymLink = FALSE;
  1959. UCHAR retryNumber = 0;
  1960. UNICODE_STRING deviceName;
  1961. UNICODE_STRING symLinkName;
  1962. DD((PCE)fdx,DDT,"P4CreatePdo - enter - PdoType= %d, DaisyChainId=%d, Ieee1284Id=<%s>\n", PdoType, DaisyChainId, Ieee1284Id);
  1963. __try {
  1964. if( !lptName ) {
  1965. DD((PCE)fdx,DDT,"P4CreatePdo - no lptName\n");
  1966. __leave;
  1967. }
  1968. DD((PCE)fdx,DDT,"P4CreatePdo - lptName = %S\n",lptName);
  1969. targetRetryDeviceName:
  1970. wstrDeviceName = P4MakePdoDeviceName( lptName, PdoType, DaisyChainId, retryNumber );
  1971. if( !wstrDeviceName ) {
  1972. DD((PCE)fdx,DDT,"P4MakePdoDeviceName FAILED\n");
  1973. __leave;
  1974. }
  1975. DD((PCE)fdx,DDT,"P4CreatePdo - wstrDeviceName = %S\n",wstrDeviceName);
  1976. RtlInitUnicodeString( &deviceName, wstrDeviceName );
  1977. status = IoCreateDevice( fdx->DriverObject,
  1978. sizeof(PDO_EXTENSION),
  1979. &deviceName,
  1980. FILE_DEVICE_PARALLEL_PORT,
  1981. FILE_DEVICE_SECURE_OPEN,
  1982. TRUE,
  1983. &pdo );
  1984. if( STATUS_SUCCESS != status ) {
  1985. DD((PCE)fdx,DDT,"P4CreatePdo - FAILED\n");
  1986. pdo = NULL; // just to make sure that we don't try to use this later
  1987. if( STATUS_OBJECT_NAME_COLLISION == status ) {
  1988. // try again with another name
  1989. DD(NULL,DDE,"P4CreatePdo - STATUS_OBJECT_NAME_COLLISION on %S\n",wstrDeviceName);
  1990. ExFreePool( wstrDeviceName );
  1991. ++retryNumber;
  1992. goto targetRetryDeviceName;
  1993. }
  1994. __leave;
  1995. }
  1996. retryNumber = 0;
  1997. targetRetrySymLink:
  1998. wstrSymLinkName = P4MakePdoSymLinkName( lptName, PdoType, DaisyChainId, retryNumber );
  1999. if( !wstrSymLinkName ) {
  2000. DD((PCE)fdx,DDT,"P4MakePdoSymLinkName FAILED\n");
  2001. __leave;
  2002. }
  2003. RtlInitUnicodeString( &symLinkName, wstrSymLinkName );
  2004. status = IoCreateUnprotectedSymbolicLink( &symLinkName , &deviceName );
  2005. if( STATUS_SUCCESS != status ) {
  2006. if( STATUS_OBJECT_NAME_COLLISION == status ) {
  2007. DD(NULL,DDE,"P4CreatePdo - STATUS_OBJECT_NAME_COLLISION on %S\n", wstrSymLinkName);
  2008. ExFreePool( wstrSymLinkName );
  2009. ++retryNumber;
  2010. goto targetRetrySymLink;
  2011. }
  2012. DD((PCE)fdx,DDT,"P4CreatePdo - create SymLink FAILED\n");
  2013. __leave;
  2014. } else {
  2015. createdSymLink = TRUE;
  2016. }
  2017. if( (NULL == Ieee1284Id) && (PdoTypeDaisyChain == PdoType) ) {
  2018. // SCM Micro device?
  2019. PPDO_EXTENSION pdx = pdo->DeviceExtension;
  2020. PPARALLEL_PORT_INFORMATION PortInfo = &fdx->PortInfo;
  2021. BOOLEAN bBuildStlDeviceId;
  2022. ULONG DeviceIdSize;
  2023. pdx->Controller = PortInfo->Controller;
  2024. bBuildStlDeviceId = ParStlCheckIfStl( pdx, DaisyChainId ) ;
  2025. if( TRUE == bBuildStlDeviceId ) {
  2026. Ieee1284Id = ParStlQueryStlDeviceId( pdx, NULL, 0, &DeviceIdSize, FALSE );
  2027. }
  2028. pdx->OriginalController = PortInfo->OriginalController;
  2029. P4InitializePdo( Fdo, pdo, PdoType, DaisyChainId, Ieee1284Id, wstrDeviceName, wstrSymLinkName );
  2030. if (Ieee1284Id) {
  2031. ExFreePool (Ieee1284Id);
  2032. Ieee1284Id = NULL;
  2033. }
  2034. } else {
  2035. P4InitializePdo( Fdo, pdo, PdoType, DaisyChainId, Ieee1284Id, wstrDeviceName, wstrSymLinkName );
  2036. }
  2037. } // __try
  2038. __finally {
  2039. if( STATUS_SUCCESS != status ) {
  2040. // failure - do cleanup
  2041. if( createdSymLink ) {
  2042. IoDeleteSymbolicLink( &symLinkName );
  2043. }
  2044. if( pdo ) {
  2045. IoDeleteDevice( pdo );
  2046. pdo = NULL;
  2047. }
  2048. if( wstrDeviceName ) {
  2049. ExFreePool( wstrDeviceName );
  2050. }
  2051. if( wstrSymLinkName ) {
  2052. ExFreePool( wstrSymLinkName );
  2053. }
  2054. }
  2055. } // __finally
  2056. return pdo;
  2057. }
  2058. VOID
  2059. P4SanitizeMultiSzId(
  2060. IN OUT PWSTR WCharBuffer,
  2061. IN ULONG BufWCharCount
  2062. )
  2063. // BufWCharCount == number of WCHARs (not bytes) in the string
  2064. //
  2065. // Sanitize the MULTI_SZ (HardwareID or CompatibleID) for PnP:
  2066. // 1) Leave UNICODE_NULLs (L'\0') alone, otherwise
  2067. // 2) Convert illegal characters to underscores (L'_')
  2068. // illegal characters are ( == L',' ) || ( <= L' ' ) || ( > (WCHAR)0x7F )
  2069. {
  2070. PWCHAR p = WCharBuffer;
  2071. ULONG i;
  2072. for( i = 0; i < BufWCharCount ; ++i, ++p ) {
  2073. if( L'\0'== *p ) {
  2074. continue;
  2075. } else if( (*p <= L' ') || (*p > (WCHAR)0x7F) || (L',' == *p) ) {
  2076. *p = L'_';
  2077. }
  2078. }
  2079. }