Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1256 lines
43 KiB

  1. /*****************************************************************************
  2. @doc INT EXT
  3. ******************************************************************************
  4. * $ProjectName: $
  5. * $ProjectRevision: $
  6. *-----------------------------------------------------------------------------
  7. * $Source: z:/pr/cmbp0/sw/cmbp0.ms/rcs/cmbp0wdm.c $
  8. * $Revision: 1.11 $
  9. *-----------------------------------------------------------------------------
  10. * $Author: WFrischauf $
  11. *-----------------------------------------------------------------------------
  12. * History: see EOF
  13. *-----------------------------------------------------------------------------
  14. *
  15. * Copyright 2000 OMNIKEY AG
  16. ******************************************************************************/
  17. #include <cmbp0wdm.h>
  18. #include <cmbp0pnp.h>
  19. #include <cmbp0scr.h>
  20. #include <cmbp0log.h>
  21. BOOLEAN DeviceSlot[CMMOB_MAX_DEVICE];
  22. // this is a list of our supported data rates
  23. ULONG SupportedDataRates[] = { 9600, 19200, 38400, 76800, 115200,
  24. 153600, 192000, 307200};
  25. // this is a list of our supported clock frequencies
  26. ULONG SupportedCLKFrequencies[] = { 4000, 8000};
  27. /*****************************************************************************
  28. DriverEntry:
  29. entry function of the driver. setup the callbacks for the OS and try to
  30. initialize a device object for every device in the system
  31. Arguments:
  32. DriverObject context of the driver
  33. RegistryPath path to the registry entry for the driver
  34. Return Value:
  35. STATUS_SUCCESS
  36. STATUS_UNSUCCESSFUL
  37. ******************************************************************************/
  38. NTSTATUS DriverEntry(
  39. PDRIVER_OBJECT DriverObject,
  40. PUNICODE_STRING RegistryPath
  41. )
  42. {
  43. NTSTATUS NTStatus = STATUS_SUCCESS;
  44. ULONG ulDevice;
  45. //#if DBG
  46. // SmartcardSetDebugLevel(DEBUG_ALL);
  47. //#endif
  48. SmartcardDebug(DEBUG_TRACE,
  49. ("%s!DriverEntry: Enter - %s %s\n",DRIVER_NAME,__DATE__,__TIME__));
  50. //
  51. // tell the system our entry points
  52. //
  53. DriverObject->MajorFunction[IRP_MJ_CREATE] = CMMOB_CreateClose;
  54. DriverObject->MajorFunction[IRP_MJ_CLOSE] = CMMOB_CreateClose;
  55. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CMMOB_DeviceIoControl;
  56. DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CMMOB_SystemControl;
  57. DriverObject->MajorFunction[IRP_MJ_CLEANUP] = CMMOB_Cleanup;
  58. DriverObject->DriverUnload = CMMOB_UnloadDriver;
  59. DriverObject->MajorFunction[IRP_MJ_PNP] = CMMOB_PnPDeviceControl;
  60. DriverObject->MajorFunction[IRP_MJ_POWER] = CMMOB_PowerDeviceControl;
  61. DriverObject->DriverExtension->AddDevice = CMMOB_AddDevice;
  62. SmartcardDebug(DEBUG_TRACE,
  63. ("%s!DriverEntry: PnP Version\n",DRIVER_NAME));
  64. SmartcardDebug(DEBUG_TRACE,
  65. ("%s!DriverEntry: Exit %x\n",DRIVER_NAME,NTStatus));
  66. return NTStatus;
  67. }
  68. /*****************************************************************************
  69. Routine Description:
  70. Trys to read the reader name from the registry
  71. Arguments:
  72. DriverObject context of call
  73. SmartcardExtension ptr to smartcard extension
  74. Return Value:
  75. none
  76. ******************************************************************************/
  77. VOID CMMOB_SetVendorAndIfdName(
  78. IN PDEVICE_OBJECT PhysicalDeviceObject,
  79. IN PSMARTCARD_EXTENSION SmartcardExtension
  80. )
  81. {
  82. RTL_QUERY_REGISTRY_TABLE parameters[3];
  83. UNICODE_STRING vendorNameU;
  84. ANSI_STRING vendorNameA;
  85. UNICODE_STRING ifdTypeU;
  86. ANSI_STRING ifdTypeA;
  87. HANDLE regKey = NULL;
  88. RtlZeroMemory (parameters, sizeof(parameters));
  89. RtlZeroMemory (&vendorNameU, sizeof(vendorNameU));
  90. RtlZeroMemory (&vendorNameA, sizeof(vendorNameA));
  91. RtlZeroMemory (&ifdTypeU, sizeof(ifdTypeU));
  92. RtlZeroMemory (&ifdTypeA, sizeof(ifdTypeA));
  93. try {
  94. //
  95. // try to read the reader name from the registry
  96. // if that does not work, we will use the default
  97. // (hardcoded) name
  98. //
  99. if (IoOpenDeviceRegistryKey(PhysicalDeviceObject,
  100. PLUGPLAY_REGKEY_DEVICE,
  101. KEY_READ,
  102. &regKey) != STATUS_SUCCESS) {
  103. SmartcardDebug(DEBUG_ERROR,
  104. ("%s!SetVendorAndIfdName: IoOpenDeviceRegistryKey failed\n",DRIVER_NAME));
  105. leave;
  106. }
  107. parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
  108. parameters[0].Name = L"VendorName";
  109. parameters[0].EntryContext = &vendorNameU;
  110. parameters[0].DefaultType = REG_SZ;
  111. parameters[0].DefaultData = &vendorNameU;
  112. parameters[0].DefaultLength = 0;
  113. parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
  114. parameters[1].Name = L"IfdType";
  115. parameters[1].EntryContext = &ifdTypeU;
  116. parameters[1].DefaultType = REG_SZ;
  117. parameters[1].DefaultData = &ifdTypeU;
  118. parameters[1].DefaultLength = 0;
  119. if (RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
  120. (PWSTR) regKey,
  121. parameters,
  122. NULL,
  123. NULL) != STATUS_SUCCESS) {
  124. SmartcardDebug(DEBUG_ERROR,
  125. ("%s!SetVendorAndIfdName: RtlQueryRegistryValues failed\n",DRIVER_NAME));
  126. leave;
  127. }
  128. if (RtlUnicodeStringToAnsiString(&vendorNameA,&vendorNameU,TRUE) != STATUS_SUCCESS) {
  129. SmartcardDebug(DEBUG_ERROR,
  130. ("%s!SetVendorAndIfdName: RtlUnicodeStringToAnsiString failed\n",DRIVER_NAME));
  131. leave;
  132. }
  133. if (RtlUnicodeStringToAnsiString(&ifdTypeA,&ifdTypeU,TRUE) != STATUS_SUCCESS) {
  134. SmartcardDebug(DEBUG_ERROR,
  135. ("%s!SetVendorAndIfdName: RtlUnicodeStringToAnsiString failed\n",DRIVER_NAME));
  136. leave;
  137. }
  138. if (vendorNameA.Length == 0 ||
  139. vendorNameA.Length > MAXIMUM_ATTR_STRING_LENGTH ||
  140. ifdTypeA.Length == 0 ||
  141. ifdTypeA.Length > MAXIMUM_ATTR_STRING_LENGTH) {
  142. SmartcardDebug(DEBUG_ERROR,
  143. ("%s!SetVendorAndIfdName: vendor name or ifdtype not found or to long\n",DRIVER_NAME));
  144. leave;
  145. }
  146. RtlCopyMemory(SmartcardExtension->VendorAttr.VendorName.Buffer,
  147. vendorNameA.Buffer,
  148. vendorNameA.Length);
  149. SmartcardExtension->VendorAttr.VendorName.Length = vendorNameA.Length;
  150. RtlCopyMemory(SmartcardExtension->VendorAttr.IfdType.Buffer,
  151. ifdTypeA.Buffer,
  152. ifdTypeA.Length);
  153. SmartcardExtension->VendorAttr.IfdType.Length = ifdTypeA.Length;
  154. SmartcardDebug(DEBUG_DRIVER,
  155. ("%s!SetVendorAndIfdName: overwritting vendor name and ifdtype\n",DRIVER_NAME));
  156. }
  157. finally {
  158. if (vendorNameU.Buffer != NULL) {
  159. RtlFreeUnicodeString(&vendorNameU);
  160. }
  161. if (vendorNameA.Buffer != NULL) {
  162. RtlFreeAnsiString(&vendorNameA);
  163. }
  164. if (ifdTypeU.Buffer != NULL) {
  165. RtlFreeUnicodeString(&ifdTypeU);
  166. }
  167. if (ifdTypeA.Buffer != NULL) {
  168. RtlFreeAnsiString(&ifdTypeA);
  169. }
  170. if (regKey != NULL) {
  171. ZwClose (regKey);
  172. }
  173. }
  174. }
  175. /*****************************************************************************
  176. Routine Description:
  177. creates a new device object for the driver, allocates & initializes all
  178. neccessary structures (i.e. SmartcardExtension & ReaderExtension).
  179. Arguments:
  180. DriverObject context of call
  181. DeviceObject ptr to the created device object
  182. Return Value:
  183. STATUS_SUCCESS
  184. STATUS_INSUFFICIENT_RESOURCES
  185. NTStatus returned by smclib.sys
  186. ******************************************************************************/
  187. NTSTATUS CMMOB_CreateDevice(
  188. IN PDRIVER_OBJECT DriverObject,
  189. IN PDEVICE_OBJECT PhysicalDeviceObject,
  190. OUT PDEVICE_OBJECT *DeviceObject
  191. )
  192. {
  193. NTSTATUS NTStatus = STATUS_SUCCESS;
  194. NTSTATUS RegStatus;
  195. RTL_QUERY_REGISTRY_TABLE ParamTable[2];
  196. UNICODE_STRING RegistryPath;
  197. UNICODE_STRING RegistryValue;
  198. WCHAR szRegValue[256];
  199. UNICODE_STRING DeviceName;
  200. UNICODE_STRING Tmp;
  201. WCHAR Buffer[64];
  202. SmartcardDebug(DEBUG_TRACE,
  203. ( "%s!CreateDevice: Enter\n",DRIVER_NAME ));
  204. try {
  205. ULONG ulDeviceInstance;
  206. PDEVICE_EXTENSION DeviceExtension;
  207. PREADER_EXTENSION ReaderExtension;
  208. PSMARTCARD_EXTENSION SmartcardExtension;
  209. *DeviceObject = NULL;
  210. for (ulDeviceInstance = 0; ulDeviceInstance < CMMOB_MAX_DEVICE; ulDeviceInstance++) {
  211. if (DeviceSlot[ulDeviceInstance] == FALSE) {
  212. DeviceSlot[ulDeviceInstance] = TRUE;
  213. break;
  214. }
  215. }
  216. if (ulDeviceInstance == CMMOB_MAX_DEVICE) {
  217. NTStatus = STATUS_INSUFFICIENT_RESOURCES;
  218. leave;
  219. }
  220. //
  221. // construct the device name
  222. //
  223. DeviceName.Buffer = Buffer;
  224. DeviceName.MaximumLength = sizeof(Buffer);
  225. DeviceName.Length = 0;
  226. RtlInitUnicodeString(&Tmp,CARDMAN_MOBILE_DEVICE_NAME);
  227. RtlCopyUnicodeString(&DeviceName,&Tmp);
  228. DeviceName.Buffer[(DeviceName.Length)/sizeof(WCHAR)-1] = L'0' + (WCHAR)ulDeviceInstance;
  229. //
  230. // Create the device object
  231. //
  232. NTStatus = IoCreateDevice(DriverObject,
  233. sizeof(DEVICE_EXTENSION),
  234. &DeviceName,
  235. FILE_DEVICE_SMARTCARD,
  236. 0,
  237. TRUE,
  238. DeviceObject);
  239. if (NTStatus != STATUS_SUCCESS) {
  240. SmartcardLogError(DriverObject,
  241. CMMOB_INSUFFICIENT_RESOURCES,
  242. NULL,
  243. 0);
  244. leave;
  245. }
  246. //
  247. // tell the OS that we supposed to do buffered io
  248. //
  249. (*DeviceObject)->Flags |= DO_BUFFERED_IO;
  250. // this is necessary, that power routine is called at IRQL_PASSIVE
  251. (*DeviceObject)->Flags |= DO_POWER_PAGABLE;
  252. // tells the IO Manager initialization is done
  253. (*DeviceObject)->Flags &= ~DO_DEVICE_INITIALIZING;
  254. //
  255. // set up the device extension.
  256. //
  257. DeviceExtension = (*DeviceObject)->DeviceExtension;
  258. RtlZeroMemory( DeviceExtension, sizeof( DEVICE_EXTENSION ));
  259. SmartcardExtension = &DeviceExtension->SmartcardExtension;
  260. // used for synchronise access to lIoCount
  261. KeInitializeSpinLock(&DeviceExtension->SpinLockIoCount);
  262. // Used for stop / start notification
  263. KeInitializeEvent(&DeviceExtension->ReaderStarted,
  264. NotificationEvent,
  265. FALSE);
  266. // Used for update thread notification after hibernation
  267. KeInitializeEvent(&DeviceExtension->CanRunUpdateThread,
  268. NotificationEvent,
  269. TRUE);
  270. //
  271. // allocate the reader extension
  272. //
  273. ReaderExtension = ExAllocatePool(NonPagedPool,sizeof( READER_EXTENSION ));
  274. if (ReaderExtension == NULL) {
  275. SmartcardLogError(DriverObject,
  276. CMMOB_INSUFFICIENT_RESOURCES,
  277. NULL,
  278. 0);
  279. NTStatus = STATUS_INSUFFICIENT_RESOURCES;
  280. leave;
  281. }
  282. RtlZeroMemory( ReaderExtension, sizeof( READER_EXTENSION ));
  283. SmartcardExtension->ReaderExtension = ReaderExtension;
  284. // ----------------------------------------------
  285. // initialize mutex
  286. // ----------------------------------------------
  287. KeInitializeMutex(&SmartcardExtension->ReaderExtension->CardManIOMutex,0L);
  288. //
  289. // enter correct version of the lib
  290. //
  291. SmartcardExtension->Version = SMCLIB_VERSION;
  292. //
  293. // setup smartcard extension - callback's
  294. //
  295. SmartcardExtension->ReaderFunction[RDF_CARD_POWER] = CMMOB_CardPower;
  296. SmartcardExtension->ReaderFunction[RDF_TRANSMIT] = CMMOB_Transmit;
  297. SmartcardExtension->ReaderFunction[RDF_SET_PROTOCOL] = CMMOB_SetProtocol;
  298. SmartcardExtension->ReaderFunction[RDF_CARD_TRACKING] = CMMOB_CardTracking;
  299. SmartcardExtension->ReaderFunction[RDF_IOCTL_VENDOR] = CMMOB_IoCtlVendor;
  300. //
  301. // setup smartcard extension - vendor attribute
  302. //
  303. // default values
  304. RtlCopyMemory(SmartcardExtension->VendorAttr.VendorName.Buffer,
  305. CMMOB_VENDOR_NAME,sizeof(CMMOB_VENDOR_NAME));
  306. SmartcardExtension->VendorAttr.VendorName.Length = sizeof(CMMOB_VENDOR_NAME);
  307. RtlCopyMemory(SmartcardExtension->VendorAttr.IfdType.Buffer,
  308. CMMOB_PRODUCT_NAME,sizeof(CMMOB_PRODUCT_NAME));
  309. SmartcardExtension->VendorAttr.IfdType.Length = sizeof(CMMOB_PRODUCT_NAME);
  310. // try to overwrite with registry values
  311. CMMOB_SetVendorAndIfdName(PhysicalDeviceObject, SmartcardExtension);
  312. SmartcardExtension->VendorAttr.UnitNo = ulDeviceInstance;
  313. SmartcardExtension->VendorAttr.IfdVersion.VersionMajor = CMMOB_MAJOR_VERSION;
  314. SmartcardExtension->VendorAttr.IfdVersion.VersionMinor = CMMOB_MINOR_VERSION;
  315. SmartcardExtension->VendorAttr.IfdVersion.BuildNumber = CMMOB_BUILD_NUMBER;
  316. SmartcardExtension->VendorAttr.IfdSerialNo.Length = 0;
  317. //
  318. // setup smartcard extension - reader capabilities
  319. //
  320. SmartcardExtension->ReaderCapabilities.SupportedProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
  321. SmartcardExtension->ReaderCapabilities.ReaderType = SCARD_READER_TYPE_PCMCIA;
  322. SmartcardExtension->ReaderCapabilities.MechProperties = 0;
  323. SmartcardExtension->ReaderCapabilities.Channel = 0;
  324. // set supported frequencies
  325. SmartcardExtension->ReaderCapabilities.CLKFrequency.Default = 4000; //not used if CLKFrequenciesSupported is supplied
  326. SmartcardExtension->ReaderCapabilities.CLKFrequency.Max = 8000; //not used if CLKFrequenciesSupported is supplied
  327. SmartcardExtension->ReaderCapabilities.CLKFrequenciesSupported.Entries =
  328. sizeof(SupportedCLKFrequencies) / sizeof(SupportedCLKFrequencies[0]);
  329. SmartcardExtension->ReaderCapabilities.CLKFrequenciesSupported.List =
  330. SupportedCLKFrequencies;
  331. // set supported baud rates
  332. SmartcardExtension->ReaderCapabilities.DataRate.Default = 9600; //not used if DataRatesSupported is supplied
  333. SmartcardExtension->ReaderCapabilities.DataRate.Max = 307200; //not used if DataRatesSupported is supplied
  334. SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries =
  335. sizeof(SupportedDataRates) / sizeof(SupportedDataRates[0]);
  336. SmartcardExtension->ReaderCapabilities.DataRatesSupported.List =
  337. SupportedDataRates;
  338. // maximum buffer size
  339. SmartcardExtension->ReaderCapabilities.MaxIFSD = 254;
  340. //
  341. // Current state of the reader
  342. //
  343. SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_UNKNOWN;
  344. SmartcardExtension->ReaderExtension->ulOldCardState = UNKNOWN;
  345. SmartcardExtension->ReaderExtension->ulNewCardState = UNKNOWN;
  346. SmartcardExtension->ReaderExtension->ulFWVersion = 100;
  347. //
  348. // Initialization of buffers
  349. //
  350. SmartcardExtension->SmartcardRequest.BufferSize = MIN_BUFFER_SIZE;
  351. SmartcardExtension->SmartcardReply.BufferSize = MIN_BUFFER_SIZE;
  352. NTStatus = SmartcardInitialize(SmartcardExtension);
  353. SmartcardExtension->ReaderExtension->ReaderPowerState = PowerReaderWorking;
  354. SmartcardExtension->ReaderExtension->CardParameters.bStopBits=2;
  355. SmartcardExtension->ReaderExtension->CardParameters.fSynchronousCard=FALSE;
  356. SmartcardExtension->ReaderExtension->CardParameters.fInversRevers=FALSE;
  357. SmartcardExtension->ReaderExtension->CardParameters.bClockFrequency=4;
  358. SmartcardExtension->ReaderExtension->CardParameters.fT0Mode=FALSE;
  359. SmartcardExtension->ReaderExtension->CardParameters.fT0Write=FALSE;
  360. SmartcardExtension->ReaderExtension->fReadCIS = FALSE;
  361. SmartcardExtension->ReaderExtension->bPreviousFlags1 = 0;
  362. if (NTStatus != STATUS_SUCCESS) {
  363. SmartcardLogError(DriverObject,
  364. CMMOB_INSUFFICIENT_RESOURCES,
  365. NULL,
  366. 0);
  367. leave;
  368. }
  369. //
  370. // tell the lib our device object & create symbolic link
  371. //
  372. SmartcardExtension->OsData->DeviceObject = *DeviceObject;
  373. if (DeviceExtension->PnPDeviceName.Buffer == NULL) {
  374. // ----------------------------------------------
  375. // register our new device
  376. // ----------------------------------------------
  377. NTStatus = IoRegisterDeviceInterface(PhysicalDeviceObject,
  378. &SmartCardReaderGuid,
  379. NULL,
  380. &DeviceExtension->PnPDeviceName);
  381. SmartcardDebug(DEBUG_DRIVER,
  382. ("%s!CreateDevice: PnPDeviceName.Buffer = %lx\n",DRIVER_NAME,
  383. DeviceExtension->PnPDeviceName.Buffer));
  384. SmartcardDebug(DEBUG_DRIVER,
  385. ("%s!CreateDevice: PnPDeviceName.BufferLength = %lx\n",DRIVER_NAME,
  386. DeviceExtension->PnPDeviceName.Length));
  387. SmartcardDebug(DEBUG_DRIVER,
  388. ("%s!CreateDevice: IoRegisterDeviceInterface returned=%lx\n",DRIVER_NAME,NTStatus));
  389. } else {
  390. SmartcardDebug(DEBUG_DRIVER,
  391. ("%s!CreateDevice: Interface already exists\n",DRIVER_NAME));
  392. }
  393. if (NTStatus != STATUS_SUCCESS) {
  394. SmartcardLogError(DriverObject,
  395. CMMOB_INSUFFICIENT_RESOURCES,
  396. NULL,
  397. 0);
  398. leave;
  399. }
  400. }
  401. finally {
  402. if (NTStatus != STATUS_SUCCESS) {
  403. CMMOB_UnloadDevice(*DeviceObject);
  404. *DeviceObject = NULL;
  405. }
  406. }
  407. SmartcardDebug(DEBUG_TRACE,
  408. ( "%s!CreateDevice: Exit %x\n",DRIVER_NAME,NTStatus ));
  409. return NTStatus;
  410. }
  411. /*****************************************************************************
  412. Routine Description:
  413. get the actual configuration from the passed FullResourceDescriptor
  414. and initializes the reader hardware
  415. Note:
  416. for an NT 4.00 build the resources must be translated by the HAL
  417. Arguments:
  418. DeviceObject context of call
  419. FullResourceDescriptor actual configuration of the reader
  420. Return Value:
  421. STATUS_SUCCESS
  422. NTStatus returned from the HAL (NT 4.00 only )
  423. NTStatus returned by LowLevel routines
  424. ******************************************************************************/
  425. NTSTATUS CMMOB_StartDevice(
  426. PDEVICE_OBJECT DeviceObject,
  427. PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor
  428. )
  429. {
  430. NTSTATUS NTStatus = STATUS_SUCCESS;
  431. PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
  432. PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
  433. PSMARTCARD_EXTENSION SmartcardExtension = &DeviceExtension->SmartcardExtension;
  434. PREADER_EXTENSION ReaderExtension = SmartcardExtension->ReaderExtension;
  435. ULONG ulCount;
  436. ULONG ulCISIndex;
  437. UCHAR bTupleCode[2];
  438. UCHAR bFirmware[2];
  439. SmartcardDebug(DEBUG_TRACE,
  440. ("%s!StartDevice: Enter\n",DRIVER_NAME));
  441. //
  442. // Get the number of resources we need
  443. //
  444. ulCount = FullResourceDescriptor->PartialResourceList.Count;
  445. PartialDescriptor = FullResourceDescriptor->PartialResourceList.PartialDescriptors;
  446. //
  447. // parse all partial descriptors
  448. //
  449. while (ulCount--) {
  450. switch (PartialDescriptor->Type) {
  451. case CmResourceTypePort:
  452. {
  453. //
  454. // Get IO-length
  455. //
  456. ReaderExtension->ulIoWindow = PartialDescriptor->u.Port.Length;
  457. ASSERT(PartialDescriptor->u.Port.Length >= 8);
  458. //
  459. // Get IO-base
  460. //
  461. #ifndef _WIN64
  462. ReaderExtension->pIoBase = (PVOID) PartialDescriptor->u.Port.Start.LowPart;
  463. #else
  464. ReaderExtension->pIoBase = (PVOID) PartialDescriptor->u.Port.Start.QuadPart;
  465. #endif
  466. SmartcardDebug(DEBUG_TRACE,
  467. ("%s!StartDevice: IoBase = %lxh\n",DRIVER_NAME,ReaderExtension->pIoBase));
  468. }
  469. break;
  470. default:
  471. break;
  472. }
  473. PartialDescriptor++;
  474. }
  475. try {
  476. //
  477. // Base initialized ?
  478. //
  479. if (ReaderExtension->pIoBase == NULL) {
  480. //
  481. // under NT 4.0 the failure of this fct for the second reader
  482. // means there is only one device
  483. //
  484. SmartcardLogError(DeviceObject,
  485. CMMOB_ERROR_MEM_PORT,
  486. NULL,
  487. 0);
  488. NTStatus = STATUS_INSUFFICIENT_RESOURCES;
  489. leave;
  490. }
  491. // initialize base addresses
  492. ReaderExtension->pbRegsBase= (PUCHAR) ReaderExtension->pIoBase;
  493. NTStatus=CMMOB_ResetReader (ReaderExtension);
  494. SmartcardDebug(DEBUG_DRIVER,
  495. ("%s!DEBUG_DRIVER: ResetReader retval = %x\n",DRIVER_NAME, NTStatus));
  496. if (NTStatus != STATUS_SUCCESS) {
  497. SmartcardLogError(DeviceObject,
  498. CMMOB_CANT_INITIALIZE_READER,
  499. NULL,
  500. 0);
  501. leave;
  502. }
  503. //
  504. // read firmware version from CIS
  505. //
  506. ReaderExtension->fReadCIS=TRUE;
  507. ReaderExtension->fTActive=TRUE;
  508. NTStatus=CMMOB_SetFlags1 (ReaderExtension);
  509. if (NTStatus != STATUS_SUCCESS) {
  510. SmartcardLogError(DeviceObject,
  511. CMMOB_CANT_INITIALIZE_READER,
  512. NULL,
  513. 0);
  514. leave;
  515. }
  516. ulCISIndex = 0;
  517. do {
  518. NTStatus=CMMOB_ReadBuffer(ReaderExtension, ulCISIndex, 2, bTupleCode);
  519. if (NTStatus != STATUS_SUCCESS) {
  520. leave;
  521. }
  522. if (bTupleCode[0] == 0x15) {
  523. // this is the version tuple
  524. // read firmware version
  525. NTStatus=CMMOB_ReadBuffer(ReaderExtension, ulCISIndex+2, 2, bFirmware);
  526. if (NTStatus != STATUS_SUCCESS) {
  527. leave;
  528. }
  529. SmartcardExtension->ReaderExtension->ulFWVersion = 100*(ULONG)bFirmware[0]+bFirmware[1];
  530. SmartcardDebug(DEBUG_TRACE,
  531. ("%s!StartDevice: Firmware version = %li\n",
  532. DRIVER_NAME, SmartcardExtension->ReaderExtension->ulFWVersion));
  533. }
  534. ulCISIndex += bTupleCode[1] + 2;
  535. }
  536. while (bTupleCode[1] != 0 &&
  537. bTupleCode[0] != 0x15 &&
  538. bTupleCode[0] != 0xFF &&
  539. ulCISIndex < CMMOB_MAX_CIS_SIZE);
  540. ReaderExtension->fReadCIS=FALSE;
  541. ReaderExtension->fTActive=FALSE;
  542. NTStatus=CMMOB_SetFlags1 (ReaderExtension);
  543. if (NTStatus != STATUS_SUCCESS) {
  544. SmartcardLogError(DeviceObject,
  545. CMMOB_CANT_INITIALIZE_READER,
  546. NULL,
  547. 0);
  548. leave;
  549. }
  550. //
  551. // start update thread
  552. //
  553. NTStatus = CMMOB_StartCardTracking(DeviceObject);
  554. // signal that the reader has been started (again)
  555. KeSetEvent(&DeviceExtension->ReaderStarted, 0, FALSE);
  556. NTStatus = IoSetDeviceInterfaceState(&DeviceExtension->PnPDeviceName,TRUE);
  557. } finally {
  558. if (NTStatus != STATUS_SUCCESS) {
  559. CMMOB_StopDevice(DeviceObject);
  560. }
  561. }
  562. SmartcardDebug(DEBUG_TRACE,
  563. ("%s!StartDevice: Exit %x\n",DRIVER_NAME,NTStatus));
  564. return NTStatus;
  565. }
  566. /*****************************************************************************
  567. Routine Description:
  568. Unmap the IO port
  569. Arguments:
  570. DeviceObject context of call
  571. Return Value:
  572. void
  573. ******************************************************************************/
  574. VOID
  575. CMMOB_StopDevice(
  576. PDEVICE_OBJECT DeviceObject
  577. )
  578. {
  579. PDEVICE_EXTENSION DeviceExtension;
  580. if (DeviceObject == NULL) {
  581. return;
  582. }
  583. SmartcardDebug(DEBUG_TRACE,
  584. ( "%s!StopDevice: Enter\n",DRIVER_NAME ));
  585. DeviceExtension = DeviceObject->DeviceExtension;
  586. KeClearEvent(&DeviceExtension->ReaderStarted);
  587. //
  588. // stop update thread
  589. //
  590. CMMOB_StopCardTracking(DeviceObject);
  591. // power down the card for saftey reasons
  592. if (DeviceExtension->SmartcardExtension.ReaderExtension->ulOldCardState == POWERED) {
  593. // we have to wait for the mutex before
  594. KeWaitForSingleObject(&DeviceExtension->SmartcardExtension.ReaderExtension->CardManIOMutex,
  595. Executive,
  596. KernelMode,
  597. FALSE,
  598. NULL);
  599. CMMOB_PowerOffCard(&DeviceExtension->SmartcardExtension);
  600. KeReleaseMutex(&DeviceExtension->SmartcardExtension.ReaderExtension->CardManIOMutex,
  601. FALSE);
  602. }
  603. //
  604. // unmap ports
  605. //
  606. if (DeviceExtension->fUnMapMem) {
  607. MmUnmapIoSpace(DeviceExtension->SmartcardExtension.ReaderExtension->pIoBase,
  608. DeviceExtension->SmartcardExtension.ReaderExtension->ulIoWindow);
  609. DeviceExtension->fUnMapMem = FALSE;
  610. }
  611. SmartcardDebug(DEBUG_TRACE,
  612. ( "%s!StopDevice: Exit\n",DRIVER_NAME ));
  613. }
  614. /*****************************************************************************
  615. Routine Description:
  616. close connections to smclib.sys and the pcmcia driver, delete symbolic
  617. link and mark the slot as unused.
  618. Arguments:
  619. DeviceObject device to unload
  620. Return Value:
  621. void
  622. ******************************************************************************/
  623. VOID CMMOB_UnloadDevice(
  624. PDEVICE_OBJECT DeviceObject
  625. )
  626. {
  627. PDEVICE_EXTENSION DeviceExtension;
  628. if (DeviceObject == NULL) {
  629. return;
  630. }
  631. SmartcardDebug(DEBUG_TRACE,
  632. ( "%s!UnloadDevice: Enter\n",DRIVER_NAME ));
  633. DeviceExtension = DeviceObject->DeviceExtension;
  634. ASSERT(DeviceExtension->SmartcardExtension.VendorAttr.UnitNo < CMMOB_MAX_DEVICE);
  635. //
  636. // Mark this slot as available
  637. //
  638. DeviceSlot[DeviceExtension->SmartcardExtension.VendorAttr.UnitNo] = FALSE;
  639. //
  640. // report to the lib that the device will be unloaded
  641. //
  642. if (DeviceExtension->SmartcardExtension.OsData != NULL) {
  643. //
  644. // finish pending tracking requests
  645. //
  646. CMMOB_CompleteCardTracking (&DeviceExtension->SmartcardExtension);
  647. }
  648. // Wait until we can safely unload the device
  649. SmartcardReleaseRemoveLockAndWait(&DeviceExtension->SmartcardExtension);
  650. SmartcardExit(&DeviceExtension->SmartcardExtension);
  651. if (DeviceExtension->PnPDeviceName.Buffer != NULL) {
  652. // disable our device so no one can open it
  653. IoSetDeviceInterfaceState(&DeviceExtension->PnPDeviceName,FALSE);
  654. RtlFreeUnicodeString(&DeviceExtension->PnPDeviceName);
  655. DeviceExtension->PnPDeviceName.Buffer = NULL;
  656. }
  657. {
  658. //
  659. // Delete the symbolic link of the smart card reader
  660. //
  661. if (DeviceExtension->LinkDeviceName.Buffer != NULL) {
  662. NTSTATUS NTStatus;
  663. NTStatus = IoDeleteSymbolicLink(&DeviceExtension->LinkDeviceName);
  664. //
  665. // we continue even if an error occurs
  666. //
  667. ASSERT(NTStatus == STATUS_SUCCESS);
  668. RtlFreeUnicodeString(&DeviceExtension->LinkDeviceName);
  669. DeviceExtension->LinkDeviceName.Buffer = NULL;
  670. }
  671. }
  672. if (DeviceExtension->SmartcardExtension.ReaderExtension != NULL) {
  673. ExFreePool(DeviceExtension->SmartcardExtension.ReaderExtension);
  674. DeviceExtension->SmartcardExtension.ReaderExtension = NULL;
  675. }
  676. //
  677. // Detach from the pcmcia driver
  678. // Under NT 4.0 we did not attach to the pcmcia driver
  679. //
  680. if (DeviceExtension->AttachedDeviceObject) {
  681. IoDetachDevice(DeviceExtension->AttachedDeviceObject);
  682. DeviceExtension->AttachedDeviceObject = NULL;
  683. }
  684. //
  685. // delete the device object
  686. //
  687. IoDeleteDevice(DeviceObject);
  688. SmartcardDebug(DEBUG_TRACE,
  689. ( "%s!UnloadDevice: Exit\n",DRIVER_NAME ));
  690. return;
  691. }
  692. /*****************************************************************************
  693. CMMOB_UnloadDriver:
  694. unloads all devices for a given driver object
  695. Arguments:
  696. DriverObject context of driver
  697. Return Value:
  698. void
  699. ******************************************************************************/
  700. VOID CMMOB_UnloadDriver(
  701. PDRIVER_OBJECT DriverObject
  702. )
  703. {
  704. PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
  705. ULONG ulSizeOfResources;
  706. SmartcardDebug(DEBUG_TRACE,
  707. ( "%s!UnloadDriver: Enter\n",DRIVER_NAME ));
  708. SmartcardDebug(DEBUG_TRACE,
  709. ( "%s!UnloadDriver: Exit\n",DRIVER_NAME ));
  710. }
  711. /*****************************************************************************
  712. CMMOB_CreateClose:
  713. allowes only one open process a time
  714. Arguments:
  715. DeviceObject context of device
  716. Irp context of call
  717. Return Value:
  718. STATUS_SUCCESS
  719. STATUS_DEVICE_BUSY
  720. ******************************************************************************/
  721. NTSTATUS CMMOB_CreateClose(
  722. PDEVICE_OBJECT DeviceObject,
  723. PIRP Irp
  724. )
  725. {
  726. NTSTATUS NTStatus = STATUS_SUCCESS;
  727. PDEVICE_EXTENSION DeviceExtension;
  728. PSMARTCARD_EXTENSION SmartcardExtension;
  729. PIO_STACK_LOCATION IrpStack;
  730. SmartcardDebug(DEBUG_TRACE,
  731. ("%s!CreateClose: Enter ",DRIVER_NAME));
  732. DeviceExtension = DeviceObject->DeviceExtension;
  733. SmartcardExtension = &DeviceExtension->SmartcardExtension;
  734. IrpStack = IoGetCurrentIrpStackLocation( Irp );
  735. //
  736. // dispatch major function
  737. //
  738. switch (IrpStack->MajorFunction) {
  739. case IRP_MJ_CREATE:
  740. SmartcardDebug(DEBUG_IOCTL,("%s!CreateClose: IRP_MJ_CREATE\n",DRIVER_NAME));
  741. NTStatus = SmartcardAcquireRemoveLock(SmartcardExtension);
  742. if (NTStatus != STATUS_SUCCESS) {
  743. // the device has been removed. Fail the call
  744. NTStatus = STATUS_DELETE_PENDING;
  745. break;
  746. }
  747. if (InterlockedIncrement(&DeviceExtension->lOpenCount) > 1) {
  748. InterlockedDecrement(&DeviceExtension->lOpenCount);
  749. NTStatus = STATUS_ACCESS_DENIED;
  750. }
  751. break;
  752. case IRP_MJ_CLOSE:
  753. SmartcardReleaseRemoveLock(SmartcardExtension);
  754. SmartcardDebug(DEBUG_IOCTL,("%s!CreateClose: IRP_MJ_CLOSE\n",DRIVER_NAME));
  755. if (InterlockedDecrement(&DeviceExtension->lOpenCount) < 0) {
  756. InterlockedIncrement(&DeviceExtension->lOpenCount);
  757. }
  758. break;
  759. default:
  760. //
  761. // unrecognized command
  762. //
  763. SmartcardDebug(DEBUG_IOCTL,("unexpected IRP\n"));
  764. NTStatus = STATUS_INVALID_DEVICE_REQUEST;
  765. break;
  766. }
  767. Irp->IoStatus.Information = 0;
  768. Irp->IoStatus.Status = NTStatus;
  769. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  770. SmartcardDebug(DEBUG_TRACE,
  771. ("%s!CreateClose: Exit %x\n",DRIVER_NAME,NTStatus));
  772. return NTStatus;
  773. }
  774. NTSTATUS CMMOB_SystemControl(
  775. PDEVICE_OBJECT DeviceObject,
  776. PIRP Irp
  777. )
  778. {
  779. PDEVICE_EXTENSION DeviceExtension;
  780. NTSTATUS status = STATUS_SUCCESS;
  781. DeviceExtension = DeviceObject->DeviceExtension;
  782. IoSkipCurrentIrpStackLocation(Irp);
  783. status = IoCallDriver(DeviceExtension->AttachedDeviceObject, Irp);
  784. return status;
  785. }
  786. /*****************************************************************************
  787. CMMOB_DeviceIoControl:
  788. all IRP's requiring IO are queued to the StartIo routine, other requests
  789. are served immediately
  790. Arguments:
  791. DeviceObject context of device
  792. Irp context of call
  793. Return Value:
  794. STATUS_SUCCESS
  795. STATUS_PENDING
  796. ******************************************************************************/
  797. NTSTATUS CMMOB_DeviceIoControl(
  798. PDEVICE_OBJECT DeviceObject,
  799. PIRP Irp
  800. )
  801. {
  802. NTSTATUS NTStatus;
  803. PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
  804. KIRQL irql;
  805. PIO_STACK_LOCATION irpSL;
  806. SmartcardDebug(DEBUG_TRACE,
  807. ("%s!DeviceIoControl: Enter\n",DRIVER_NAME));
  808. irpSL = IoGetCurrentIrpStackLocation(Irp);
  809. #if DBG
  810. switch (irpSL->Parameters.DeviceIoControl.IoControlCode) {
  811. case IOCTL_SMARTCARD_EJECT:
  812. SmartcardDebug(DEBUG_IOCTL,
  813. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_EJECT"));
  814. break;
  815. case IOCTL_SMARTCARD_GET_ATTRIBUTE:
  816. SmartcardDebug(DEBUG_IOCTL,
  817. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_GET_ATTRIBUTE"));
  818. break;
  819. case IOCTL_SMARTCARD_GET_LAST_ERROR:
  820. SmartcardDebug(DEBUG_IOCTL,
  821. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_GET_LAST_ERROR"));
  822. break;
  823. case IOCTL_SMARTCARD_GET_STATE:
  824. SmartcardDebug(DEBUG_IOCTL,
  825. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_GET_STATE"));
  826. break;
  827. case IOCTL_SMARTCARD_IS_ABSENT:
  828. SmartcardDebug(DEBUG_IOCTL,
  829. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_IS_ABSENT"));
  830. break;
  831. case IOCTL_SMARTCARD_IS_PRESENT:
  832. SmartcardDebug(DEBUG_IOCTL,
  833. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_IS_PRESENT"));
  834. break;
  835. case IOCTL_SMARTCARD_POWER:
  836. SmartcardDebug(DEBUG_IOCTL,
  837. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_POWER"));
  838. break;
  839. case IOCTL_SMARTCARD_SET_ATTRIBUTE:
  840. SmartcardDebug(DEBUG_IOCTL,
  841. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_SET_ATTRIBUTE"));
  842. break;
  843. case IOCTL_SMARTCARD_SET_PROTOCOL:
  844. SmartcardDebug(DEBUG_IOCTL,
  845. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_SET_PROTOCOL"));
  846. break;
  847. case IOCTL_SMARTCARD_SWALLOW:
  848. SmartcardDebug(DEBUG_IOCTL,
  849. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_SWALLOW"));
  850. break;
  851. case IOCTL_SMARTCARD_TRANSMIT:
  852. SmartcardDebug(DEBUG_IOCTL,
  853. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "IOCTL_SMARTCARD_TRANSMIT"));
  854. break;
  855. default:
  856. SmartcardDebug(DEBUG_IOCTL,
  857. ("%s!DeviceIoControl: %s\n", DRIVER_NAME, "Vendor specific or unexpected IOCTL"));
  858. break;
  859. }
  860. #endif
  861. NTStatus = SmartcardAcquireRemoveLock(&DeviceExtension->SmartcardExtension);
  862. if (!NT_SUCCESS(NTStatus)) {
  863. // the device has been removed. Fail the call
  864. NTStatus = STATUS_DELETE_PENDING;
  865. Irp->IoStatus.Status = NTStatus;
  866. Irp->IoStatus.Information = 0;
  867. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  868. SmartcardDebug(DEBUG_TRACE,
  869. ("%s!DeviceIoControl: Exit %x\n",DRIVER_NAME,NTStatus));
  870. return NTStatus;
  871. }
  872. KeAcquireSpinLock(&DeviceExtension->SpinLockIoCount, &irql);
  873. if (DeviceExtension->lIoCount == 0) {
  874. KeReleaseSpinLock(&DeviceExtension->SpinLockIoCount, irql);
  875. NTStatus = KeWaitForSingleObject(&DeviceExtension->ReaderStarted,
  876. Executive,
  877. KernelMode,
  878. FALSE,
  879. NULL);
  880. ASSERT(NTStatus == STATUS_SUCCESS);
  881. KeAcquireSpinLock(&DeviceExtension->SpinLockIoCount, &irql);
  882. }
  883. ASSERT(DeviceExtension->lIoCount >= 0);
  884. DeviceExtension->lIoCount++;
  885. KeReleaseSpinLock(&DeviceExtension->SpinLockIoCount, irql);
  886. KeWaitForSingleObject(&DeviceExtension->SmartcardExtension.ReaderExtension->CardManIOMutex,
  887. Executive,
  888. KernelMode,
  889. FALSE,
  890. NULL);
  891. // get current card state
  892. NTStatus = CMMOB_UpdateCurrentState(&DeviceExtension->SmartcardExtension);
  893. NTStatus = SmartcardDeviceControl(&DeviceExtension->SmartcardExtension,Irp);
  894. KeReleaseMutex(&DeviceExtension->SmartcardExtension.ReaderExtension->CardManIOMutex,
  895. FALSE);
  896. KeAcquireSpinLock(&DeviceExtension->SpinLockIoCount, &irql);
  897. DeviceExtension->lIoCount--;
  898. ASSERT(DeviceExtension->lIoCount >= 0);
  899. KeReleaseSpinLock(&DeviceExtension->SpinLockIoCount, irql);
  900. SmartcardReleaseRemoveLock(&DeviceExtension->SmartcardExtension);
  901. SmartcardDebug(DEBUG_TRACE,
  902. ("%s!DeviceIoControl: Exit %x\n",DRIVER_NAME,NTStatus));
  903. return NTStatus;
  904. }
  905. /*****************************************************************************
  906. Routine Description:
  907. This routine is called by the I/O system when the calling thread terminates
  908. Arguments:
  909. DeviceObject - Pointer to device object for this miniport
  910. Irp - IRP involved.
  911. Return Value:
  912. STATUS_CANCELLED
  913. ******************************************************************************/
  914. NTSTATUS CMMOB_Cleanup(
  915. IN PDEVICE_OBJECT DeviceObject,
  916. IN PIRP Irp
  917. )
  918. {
  919. PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
  920. PSMARTCARD_EXTENSION SmartcardExtension = &DeviceExtension->SmartcardExtension;
  921. SmartcardDebug(DEBUG_TRACE,
  922. ("%s!Cleanup: Enter\n",DRIVER_NAME));
  923. if (SmartcardExtension->ReaderExtension != NULL &&
  924. // if the device has been removed ReaderExtension == NULL
  925. DeviceExtension->lOpenCount == 1 )
  926. // complete card tracking only if this is the the last close call
  927. // otherwise the card tracking of the resource manager is canceled
  928. {
  929. //
  930. // We need to complete the notification irp
  931. //
  932. CMMOB_CompleteCardTracking(SmartcardExtension);
  933. }
  934. SmartcardDebug(DEBUG_DRIVER,
  935. ("%s!Cleanup: Completing IRP %lx\n",DRIVER_NAME,Irp));
  936. Irp->IoStatus.Information = 0;
  937. Irp->IoStatus.Status = STATUS_SUCCESS;
  938. IoCompleteRequest(Irp,IO_NO_INCREMENT);
  939. SmartcardDebug(DEBUG_TRACE,
  940. ("%s!Cleanup: Exit\n",DRIVER_NAME));
  941. return STATUS_SUCCESS;
  942. }
  943. /*****************************************************************************
  944. SysDelay:
  945. performs a required delay. The usage of KeStallExecutionProcessor is
  946. very nasty, but it happends only if SysDelay is called in the context of
  947. our DPC routine (which is only called if a card change was detected).
  948. For 'normal' IO we have Irql < DISPATCH_LEVEL, so if the reader is polled
  949. while waiting for response we will not block the entire system
  950. Arguments:
  951. Timeout delay in milli seconds
  952. Return Value:
  953. void
  954. ******************************************************************************/
  955. VOID SysDelay(
  956. ULONG Timeout
  957. )
  958. {
  959. LARGE_INTEGER SysTimeout;
  960. if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
  961. ULONG Cnt = 20 * Timeout;
  962. SmartcardDebug(DEBUG_DRIVER,
  963. ("%s! Waiting at IRQL >= DISPATCH_LEVEL %l\n",DRIVER_NAME,Timeout));
  964. while (Cnt--) {
  965. //
  966. // KeStallExecutionProcessor: counted in us
  967. //
  968. KeStallExecutionProcessor( 50 );
  969. }
  970. } else {
  971. SysTimeout = RtlConvertLongToLargeInteger(Timeout * -10000L);
  972. //
  973. // KeDelayExecutionThread: counted in 100 ns
  974. //
  975. KeDelayExecutionThread( KernelMode, FALSE, &SysTimeout );
  976. }
  977. return;
  978. }
  979. /*****************************************************************************
  980. * History:
  981. * $Log: cmbp0wdm.c $
  982. * Revision 1.11 2001/01/22 08:12:22 WFrischauf
  983. * No comment given
  984. *
  985. * Revision 1.9 2000/09/25 14:24:33 WFrischauf
  986. * No comment given
  987. *
  988. * Revision 1.8 2000/08/24 09:05:14 TBruendl
  989. * No comment given
  990. *
  991. * Revision 1.7 2000/08/16 16:52:17 WFrischauf
  992. * No comment given
  993. *
  994. * Revision 1.6 2000/08/09 12:46:01 WFrischauf
  995. * No comment given
  996. *
  997. * Revision 1.5 2000/07/27 13:53:06 WFrischauf
  998. * No comment given
  999. *
  1000. *
  1001. ******************************************************************************/