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.

561 lines
14 KiB

  1. /*
  2. * UNIMODEM "Fakemodem" controllerless driver illustrative example
  3. *
  4. * (C) 2000 Microsoft Corporation
  5. * All Rights Reserved
  6. *
  7. */
  8. #include "fakemodem.h"
  9. #if DBG
  10. ULONG DebugFlags=255;
  11. #endif
  12. UNICODE_STRING DriverEntryRegPath;
  13. #ifdef ALLOC_PRAGMA
  14. #pragma alloc_text(INIT,DriverEntry)
  15. #pragma alloc_text(PAGE,FakeModemAddDevice)
  16. #endif
  17. NTSTATUS
  18. DriverEntry(
  19. IN PDRIVER_OBJECT DriverObject,
  20. IN PUNICODE_STRING RegistryPath
  21. )
  22. {
  23. NTSTATUS status;
  24. RTL_QUERY_REGISTRY_TABLE paramTable[3];
  25. ULONG zero = 0;
  26. ULONG debugLevel = 0;
  27. ULONG shouldBreak = 0;
  28. PWCHAR path;
  29. D_INIT(DbgPrint("FAKEMODEM: DriverEntry\n");)
  30. // Since the registry path parameter is a "counted" UNICODE string, it
  31. // might not be zero terminated. For a very short time allocate memory
  32. // to hold the registry path zero terminated so that we can use it to
  33. // delve into the registry.
  34. path = ALLOCATE_PAGED_POOL(RegistryPath->Length+sizeof(WCHAR));
  35. if (path != NULL)
  36. {
  37. RtlZeroMemory(&paramTable[0],sizeof(paramTable));
  38. RtlZeroMemory(path,RegistryPath->Length+sizeof(WCHAR));
  39. RtlMoveMemory(path,RegistryPath->Buffer,RegistryPath->Length);
  40. paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
  41. paramTable[0].Name = L"BreakOnEntry";
  42. paramTable[0].EntryContext = &shouldBreak;
  43. paramTable[0].DefaultType = REG_DWORD;
  44. paramTable[0].DefaultData = &zero;
  45. paramTable[0].DefaultLength = sizeof(ULONG);
  46. paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
  47. paramTable[1].Name = L"DebugFlags";
  48. paramTable[1].EntryContext = &debugLevel;
  49. paramTable[1].DefaultType = REG_DWORD;
  50. paramTable[1].DefaultData = &zero;
  51. paramTable[1].DefaultLength = sizeof(ULONG);
  52. // If the Debugflags registry key is not set then
  53. // provide full debugging information
  54. if (!NT_SUCCESS(RtlQueryRegistryValues(
  55. RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
  56. path, &paramTable[0], NULL, NULL)))
  57. {
  58. shouldBreak = 0;
  59. debugLevel = 255;
  60. }
  61. FREE_POOL(path);
  62. }
  63. #if DBG
  64. DebugFlags = debugLevel;
  65. #endif
  66. if (shouldBreak)
  67. {
  68. DbgBreakPoint();
  69. }
  70. // Pnp driver entry point
  71. DriverObject->DriverExtension->AddDevice = FakeModemAddDevice;
  72. // Initialize the driver object with driver's entry points
  73. DriverObject->DriverUnload = FakeModemUnload;
  74. DriverObject->MajorFunction[IRP_MJ_CREATE] = FakeModemOpen;
  75. DriverObject->MajorFunction[IRP_MJ_CLOSE] = FakeModemClose;
  76. DriverObject->MajorFunction[IRP_MJ_CLEANUP] = FakeModemCleanup;
  77. DriverObject->MajorFunction[IRP_MJ_WRITE] = FakeModemWrite;
  78. DriverObject->MajorFunction[IRP_MJ_READ] = FakeModemRead;
  79. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FakeModemIoControl;
  80. DriverObject->MajorFunction[IRP_MJ_PNP] = FakeModemPnP;
  81. DriverObject->MajorFunction[IRP_MJ_POWER] = FakeModemPower;
  82. D_INIT(DbgPrint("FAKEMODEM: End of DriverEntry\n");)
  83. return STATUS_SUCCESS;
  84. }
  85. VOID
  86. FakeModemUnload(
  87. IN PDRIVER_OBJECT DriverObject
  88. )
  89. {
  90. D_INIT(DbgPrint("FAKEMODEM: FakeModemUnload()\n");)
  91. return;
  92. }
  93. NTSTATUS
  94. FakeModemAddDevice(
  95. IN PDRIVER_OBJECT DriverObject,
  96. IN PDEVICE_OBJECT Pdo
  97. )
  98. {
  99. NTSTATUS status=STATUS_SUCCESS;
  100. PDEVICE_OBJECT Fdo;
  101. PDEVICE_EXTENSION DeviceExtension;
  102. UNICODE_STRING DeviceName;
  103. D_INIT(DbgPrint("FAKEMODEM: Fakemodem Add Device\n");)
  104. // Create our functional device object (FDO)
  105. status=IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL,
  106. FILE_DEVICE_SERIAL_PORT, FILE_AUTOGENERATED_DEVICE_NAME,
  107. FALSE, &Fdo);
  108. if (status != STATUS_SUCCESS)
  109. {
  110. return status;
  111. }
  112. Fdo->Flags |= DO_BUFFERED_IO;
  113. DeviceExtension = Fdo->DeviceExtension;
  114. DeviceExtension->DeviceObject = Fdo;
  115. // Attach our FDO to the PDO supplied
  116. DeviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(Fdo, Pdo);
  117. if (NULL == DeviceExtension->LowerDevice)
  118. {
  119. // Could not attach
  120. IoDeleteDevice(Fdo);
  121. return STATUS_UNSUCCESSFUL;
  122. }
  123. // Try to create a ComX for it. don't care if it fails
  124. // modem.sys creates a name for device that unimodem will use
  125. FakeModemHandleSymbolicLink(Pdo, TRUE,
  126. &DeviceExtension->InterfaceNameString, Fdo);
  127. // Initialise the spinlock
  128. KeInitializeSpinLock(&DeviceExtension->SpinLock);
  129. // Initialize the device extension
  130. DeviceExtension->ReferenceCount=1;
  131. DeviceExtension->Removing=FALSE;
  132. DeviceExtension->Started=FALSE;
  133. DeviceExtension->OpenCount=0;
  134. KeInitializeEvent(&DeviceExtension->RemoveEvent, NotificationEvent, FALSE);
  135. // Initialize the read and write queues
  136. InitializeListHead(&DeviceExtension->ReadQueue);
  137. DeviceExtension->CurrentReadIrp=NULL;
  138. InitializeListHead(&DeviceExtension->WriteQueue);
  139. DeviceExtension->CurrentWriteIrp=NULL;
  140. InitializeListHead(&DeviceExtension->MaskQueue);
  141. DeviceExtension->CurrentMaskIrp=NULL;
  142. // Clear this flag so the device object can be used
  143. Fdo->Flags &= ~(DO_DEVICE_INITIALIZING);
  144. return STATUS_SUCCESS;
  145. }
  146. NTSTATUS
  147. GetRegistryKeyValue (
  148. IN HANDLE Handle,
  149. IN PWCHAR KeyNameString,
  150. IN ULONG KeyNameStringLength,
  151. IN PVOID Data,
  152. IN ULONG DataLength
  153. )
  154. {
  155. UNICODE_STRING keyName;
  156. ULONG length;
  157. PKEY_VALUE_FULL_INFORMATION fullInfo;
  158. NTSTATUS ntStatus = STATUS_NO_MEMORY;
  159. RtlInitUnicodeString (&keyName, KeyNameString);
  160. length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength +
  161. DataLength;
  162. fullInfo = ExAllocatePool(PagedPool, length);
  163. if (fullInfo)
  164. {
  165. ntStatus = ZwQueryValueKey(Handle, &keyName,
  166. KeyValueFullInformation, fullInfo, length, &length);
  167. if (NT_SUCCESS(ntStatus))
  168. {
  169. // If there is enough room in the data buffer, copy the output
  170. if (DataLength >= fullInfo->DataLength)
  171. {
  172. RtlCopyMemory(Data,
  173. ((PUCHAR) fullInfo) + fullInfo->DataOffset,
  174. fullInfo->DataLength);
  175. }
  176. }
  177. ExFreePool(fullInfo);
  178. }
  179. return ntStatus;
  180. }
  181. NTSTATUS
  182. FakeModemHandleSymbolicLink(
  183. PDEVICE_OBJECT Pdo,
  184. BOOLEAN Create,
  185. PUNICODE_STRING InterfaceName,
  186. PDEVICE_OBJECT Fdo
  187. )
  188. {
  189. UNICODE_STRING SymbolicLink;
  190. ULONG StringLength;
  191. NTSTATUS Status;
  192. WCHAR ComPort[80];
  193. HANDLE keyHandle;
  194. RTL_QUERY_REGISTRY_TABLE paramTable[1];
  195. D_INIT(DbgPrint("FAKEMODEM: HandleSymbolicLink\n");)
  196. Status = IoOpenDeviceRegistryKey(Pdo, PLUGPLAY_REGKEY_DEVICE,
  197. STANDARD_RIGHTS_READ, &keyHandle);
  198. SymbolicLink.Length=0;
  199. SymbolicLink.MaximumLength=sizeof(WCHAR)*256;
  200. SymbolicLink.Buffer=ExAllocatePool(PagedPool,
  201. SymbolicLink.MaximumLength+sizeof(WCHAR));
  202. if (SymbolicLink.Buffer == NULL)
  203. {
  204. ZwClose(keyHandle);
  205. return STATUS_INSUFFICIENT_RESOURCES;
  206. }
  207. RtlZeroMemory(SymbolicLink.Buffer, SymbolicLink.MaximumLength);
  208. RtlAppendUnicodeToString(&SymbolicLink, L"\\");
  209. RtlAppendUnicodeToString(&SymbolicLink, OBJECT_DIRECTORY);
  210. RtlAppendUnicodeToString(&SymbolicLink, L"\\");
  211. Status=GetRegistryKeyValue(keyHandle, L"PortName",
  212. sizeof(L"PortName"), ComPort, sizeof(ComPort));
  213. D_INIT(DbgPrint("FAKEMODEM: PortName %ws\n",ComPort);)
  214. if (Status != STATUS_SUCCESS)
  215. {
  216. ExFreePool(SymbolicLink.Buffer);
  217. ZwClose(keyHandle);
  218. return Status;
  219. }
  220. RtlAppendUnicodeToString(&SymbolicLink, ComPort);
  221. ZwClose(keyHandle);
  222. if (Create)
  223. {
  224. UNICODE_STRING PdoName;
  225. PdoName.Length=0;
  226. PdoName.MaximumLength=sizeof(WCHAR)*256;
  227. PdoName.Buffer=ExAllocatePool(PagedPool,
  228. PdoName.MaximumLength+sizeof(WCHAR));
  229. if (PdoName.Buffer == NULL)
  230. {
  231. ExFreePool(SymbolicLink.Buffer);
  232. return STATUS_INSUFFICIENT_RESOURCES;
  233. }
  234. RtlZeroMemory(PdoName.Buffer,PdoName.MaximumLength);
  235. Status=IoGetDeviceProperty(Pdo, DevicePropertyPhysicalDeviceObjectName,
  236. (ULONG)PdoName.MaximumLength, PdoName.Buffer, &StringLength);
  237. if (!NT_SUCCESS(Status))
  238. {
  239. D_INIT(DbgPrint("FAKEMODEM: IoGetDeviceProperty() failed %08lx\n",
  240. Status);)
  241. ExFreePool(SymbolicLink.Buffer);
  242. return Status;
  243. }
  244. PdoName.Length+=(USHORT)StringLength-sizeof(UNICODE_NULL);
  245. D_INIT(DbgPrint("FAKEMODEM: PdoName: %ws\n",PdoName.Buffer);)
  246. Status=IoCreateSymbolicLink(&SymbolicLink, &PdoName);
  247. Status=IoRegisterDeviceInterface(Pdo, &GUID_CLASS_MODEM, NULL,
  248. InterfaceName);
  249. if (NT_SUCCESS(Status))
  250. {
  251. IoSetDeviceInterfaceState(InterfaceName, TRUE);
  252. } else
  253. {
  254. D_INIT(DbgPrint("FAKEMODEM: IoRegisterDeviceInterface() failed %08lx\n",Status);)
  255. }
  256. Status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM",
  257. PdoName.Buffer, REG_SZ, ComPort,
  258. (wcslen(ComPort) + 1) * sizeof(WCHAR));
  259. if (!NT_SUCCESS(Status))
  260. {
  261. D_INIT(DbgPrint("FAKEMODEM: RtlWriteRegistryValue() failed %08lx\n",Status);)
  262. ExFreePool(SymbolicLink.Buffer);
  263. ExFreePool(PdoName.Buffer);
  264. return Status;
  265. }
  266. ExFreePool(PdoName.Buffer);
  267. } else {
  268. Status=IoDeleteSymbolicLink(&SymbolicLink);
  269. D_INIT(DbgPrint("FAKEMODEM: Deleted symbolic link\n");)
  270. }
  271. ExFreePool(SymbolicLink.Buffer);
  272. D_INIT(DbgPrint("FAKEMODEM: End of handle symbolic link\n");)
  273. return Status;
  274. }
  275. NTSTATUS
  276. QueryDeviceCaps(
  277. PDEVICE_OBJECT Pdo,
  278. PDEVICE_CAPABILITIES Capabilities
  279. )
  280. {
  281. PDEVICE_OBJECT deviceObject=Pdo;
  282. PIRP irp;
  283. PIO_STACK_LOCATION NextSp;
  284. KEVENT Event;
  285. NTSTATUS Status;
  286. // Get a pointer to the top most device object in the stack of
  287. // devices, beginning with the deviceObject.
  288. while (deviceObject->AttachedDevice)
  289. {
  290. deviceObject = deviceObject->AttachedDevice;
  291. }
  292. // Begin by allocating the IRP for this request. Do not charge
  293. // quota to the current process for this IRP.
  294. irp = IoAllocateIrp(
  295. #if DBG
  296. (UCHAR)(deviceObject->StackSize+1),
  297. #else
  298. deviceObject->StackSize,
  299. #endif
  300. FALSE);
  301. if (irp == NULL)
  302. {
  303. return STATUS_INSUFFICIENT_RESOURCES;
  304. }
  305. #if DBG
  306. {
  307. // Setup a current stack location, so the debug code can see the
  308. // MJ value
  309. PIO_STACK_LOCATION irpSp=IoGetNextIrpStackLocation(irp);
  310. irpSp->MajorFunction=IRP_MJ_PNP;
  311. IoSetNextIrpStackLocation(irp);
  312. }
  313. #endif
  314. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  315. irp->IoStatus.Information = 0;
  316. RtlZeroMemory(Capabilities,sizeof(DEVICE_CAPABILITIES));
  317. Capabilities->Size=sizeof(DEVICE_CAPABILITIES);
  318. Capabilities->Version=1;
  319. Capabilities->Address=-1;
  320. Capabilities->UINumber=-1;
  321. // Get a pointer to the stack location of the first driver which will be
  322. // invoked. This is where the function codes and parameters are set.
  323. NextSp = IoGetNextIrpStackLocation(irp);
  324. NextSp->MajorFunction=IRP_MJ_PNP;
  325. NextSp->MinorFunction=IRP_MN_QUERY_CAPABILITIES;
  326. NextSp->Parameters.DeviceCapabilities.Capabilities=Capabilities;
  327. Status=WaitForLowerDriverToCompleteIrp(deviceObject, irp, FALSE );
  328. IoFreeIrp(irp);
  329. return Status;
  330. }
  331. NTSTATUS
  332. ModemSetRegistryKeyValue(
  333. IN PDEVICE_OBJECT Pdo,
  334. IN ULONG DevInstKeyType,
  335. IN PWCHAR KeyNameString,
  336. IN ULONG DataType,
  337. IN PVOID Data,
  338. IN ULONG DataLength)
  339. {
  340. NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  341. HANDLE Handle;
  342. UNICODE_STRING keyName;
  343. PAGED_CODE();
  344. D_ERROR(DbgPrint("MODEM: Current IRQL %d\n",KeGetCurrentIrql());)
  345. ntStatus = IoOpenDeviceRegistryKey(Pdo, DevInstKeyType, KEY_ALL_ACCESS,
  346. &Handle);
  347. if (NT_SUCCESS(ntStatus))
  348. {
  349. RtlInitUnicodeString(&keyName,KeyNameString);
  350. ntStatus = ZwSetValueKey(Handle, &keyName, 0, DataType, Data,
  351. DataLength);
  352. if (!NT_SUCCESS(ntStatus))
  353. {
  354. D_ERROR(DbgPrint("MODEM: Could not set value, %08lx\n",ntStatus);)
  355. }
  356. } else
  357. {
  358. ZwClose(Handle);
  359. D_ERROR(DbgPrint("MODEM: Could not open dev registry key, %08lx\n",
  360. ntStatus);)
  361. }
  362. return ntStatus;
  363. }
  364. NTSTATUS
  365. ModemGetRegistryKeyValue (
  366. IN PDEVICE_OBJECT Pdo,
  367. IN ULONG DevInstKeyType,
  368. IN PWCHAR KeyNameString,
  369. IN PVOID Data,
  370. IN ULONG DataLength
  371. )
  372. {
  373. UNICODE_STRING keyName;
  374. ULONG length;
  375. PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
  376. NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
  377. HANDLE Handle;
  378. PAGED_CODE();
  379. ntStatus = IoOpenDeviceRegistryKey(Pdo, DevInstKeyType,
  380. STANDARD_RIGHTS_READ, &Handle);
  381. if (NT_SUCCESS(ntStatus))
  382. {
  383. RtlInitUnicodeString (&keyName, KeyNameString);
  384. length = sizeof(KEY_VALUE_FULL_INFORMATION) + DataLength;
  385. PartialInfo = ALLOCATE_PAGED_POOL(length);
  386. if (PartialInfo)
  387. {
  388. ntStatus = ZwQueryValueKey (Handle, &keyName,
  389. KeyValuePartialInformation, PartialInfo, length, &length);
  390. if (NT_SUCCESS(ntStatus))
  391. {
  392. //
  393. // If there is enough room in the data buffer, copy the output
  394. //
  395. if (DataLength >= PartialInfo->DataLength)
  396. {
  397. RtlCopyMemory (Data, PartialInfo->Data,
  398. PartialInfo->DataLength);
  399. }
  400. } else
  401. {
  402. D_ERROR(DbgPrint("MODEM: could not query value, %08lx\n",
  403. ntStatus);)
  404. }
  405. FREE_POOL(PartialInfo);
  406. }
  407. ZwClose(Handle);
  408. } else {
  409. D_ERROR(DbgPrint("MODEM: could open device reg key, %08lx\n",ntStatus);)
  410. }
  411. return ntStatus;
  412. }