1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Keyboard class driver 4 * FILE: drivers/kbdclass/kbdclass.c 5 * PURPOSE: Keyboard class driver 6 * 7 * PROGRAMMERS: Herv� Poussineau (hpoussin@reactos.org) 8 */ 9 10 #include "kbdclass.h" 11 12 #include <stdio.h> 13 #include <pseh/pseh2.h> 14 #include <kbdmou.h> 15 #include <debug.h> 16 17 static DRIVER_UNLOAD DriverUnload; 18 static DRIVER_DISPATCH ClassCreate; 19 static DRIVER_DISPATCH ClassClose; 20 static DRIVER_DISPATCH ClassCleanup; 21 static DRIVER_DISPATCH ClassRead; 22 static DRIVER_DISPATCH ClassDeviceControl; 23 static DRIVER_DISPATCH ClassPower; 24 static DRIVER_ADD_DEVICE ClassAddDevice; 25 static DRIVER_STARTIO ClassStartIo; 26 static DRIVER_CANCEL ClassCancelRoutine; 27 static NTSTATUS 28 HandleReadIrp( 29 IN PDEVICE_OBJECT DeviceObject, 30 IN PIRP Irp, 31 BOOLEAN IsInStartIo); 32 33 static VOID NTAPI 34 DriverUnload(IN PDRIVER_OBJECT DriverObject) 35 { 36 // nothing to do here yet 37 } 38 39 static NTSTATUS NTAPI 40 ClassCreate( 41 IN PDEVICE_OBJECT DeviceObject, 42 IN PIRP Irp) 43 { 44 TRACE_(CLASS_NAME, "IRP_MJ_CREATE\n"); 45 46 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO) 47 return ForwardIrpAndForget(DeviceObject, Irp); 48 49 /* FIXME: open all associated Port devices */ 50 Irp->IoStatus.Status = STATUS_SUCCESS; 51 Irp->IoStatus.Information = 0; 52 IoCompleteRequest(Irp, IO_NO_INCREMENT); 53 return STATUS_SUCCESS; 54 } 55 56 static NTSTATUS NTAPI 57 ClassClose( 58 IN PDEVICE_OBJECT DeviceObject, 59 IN PIRP Irp) 60 { 61 TRACE_(CLASS_NAME, "IRP_MJ_CLOSE\n"); 62 63 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO) 64 return ForwardIrpAndForget(DeviceObject, Irp); 65 66 /* FIXME: close all associated Port devices */ 67 Irp->IoStatus.Status = STATUS_SUCCESS; 68 Irp->IoStatus.Information = 0; 69 IoCompleteRequest(Irp, IO_NO_INCREMENT); 70 return STATUS_SUCCESS; 71 } 72 73 static NTSTATUS NTAPI 74 ClassCleanup( 75 IN PDEVICE_OBJECT DeviceObject, 76 IN PIRP Irp) 77 { 78 TRACE_(CLASS_NAME, "IRP_MJ_CLEANUP\n"); 79 80 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO) 81 return ForwardIrpAndForget(DeviceObject, Irp); 82 83 /* FIXME: cleanup all associated Port devices */ 84 Irp->IoStatus.Status = STATUS_SUCCESS; 85 Irp->IoStatus.Information = 0; 86 IoCompleteRequest(Irp, IO_NO_INCREMENT); 87 return STATUS_SUCCESS; 88 } 89 90 static NTSTATUS NTAPI 91 ClassRead( 92 IN PDEVICE_OBJECT DeviceObject, 93 IN PIRP Irp) 94 { 95 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 96 KIRQL OldIrql; 97 NTSTATUS Status; 98 99 TRACE_(CLASS_NAME, "IRP_MJ_READ\n"); 100 101 ASSERT(DeviceExtension->Common.IsClassDO); 102 103 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO) 104 return ForwardIrpAndForget(DeviceObject, Irp); 105 106 if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length < sizeof(KEYBOARD_INPUT_DATA)) 107 { 108 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; 109 Irp->IoStatus.Information = 0; 110 IoCompleteRequest(Irp, IO_NO_INCREMENT); 111 112 return STATUS_BUFFER_TOO_SMALL; 113 } 114 115 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql); 116 Status = HandleReadIrp(DeviceObject, Irp, FALSE); 117 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql); 118 return Status; 119 } 120 121 static NTSTATUS NTAPI 122 ClassDeviceControl( 123 IN PDEVICE_OBJECT DeviceObject, 124 IN PIRP Irp) 125 { 126 //PCLASS_DEVICE_EXTENSION DeviceExtension; 127 NTSTATUS Status = STATUS_NOT_SUPPORTED; 128 129 TRACE_(CLASS_NAME, "IRP_MJ_DEVICE_CONTROL\n"); 130 131 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO) 132 return ForwardIrpAndForget(DeviceObject, Irp); 133 134 //DeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 135 136 switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode) 137 { 138 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES: 139 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: 140 case IOCTL_KEYBOARD_QUERY_INDICATORS: 141 case IOCTL_KEYBOARD_QUERY_TYPEMATIC: 142 { 143 /* FIXME: We hope that all devices will return the same result. 144 * Ask only the first one */ 145 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead; 146 if (Head->Flink != Head) 147 { 148 /* We have at least one device */ 149 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Head->Flink, PORT_DEVICE_EXTENSION, ListEntry); 150 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; 151 IoSkipCurrentIrpStackLocation(Irp); 152 return IoCallDriver(DevExt->DeviceObject, Irp); 153 } 154 break; 155 } 156 case IOCTL_KEYBOARD_SET_INDICATORS: 157 case IOCTL_KEYBOARD_SET_TYPEMATIC: /* not in MSDN, would seem logical */ 158 { 159 /* Send it to all associated Port devices */ 160 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead; 161 PLIST_ENTRY Entry = Head->Flink; 162 Status = STATUS_SUCCESS; 163 while (Entry != Head) 164 { 165 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Entry, PORT_DEVICE_EXTENSION, ListEntry); 166 NTSTATUS IntermediateStatus; 167 168 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; 169 IntermediateStatus = ForwardIrpAndWait(DevExt->DeviceObject, Irp); 170 if (!NT_SUCCESS(IntermediateStatus)) 171 Status = IntermediateStatus; 172 Entry = Entry->Flink; 173 } 174 break; 175 } 176 default: 177 WARN_(CLASS_NAME, "IRP_MJ_DEVICE_CONTROL / unknown I/O control code 0x%lx\n", 178 IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode); 179 ASSERT(FALSE); 180 break; 181 } 182 183 Irp->IoStatus.Status = Status; 184 Irp->IoStatus.Information = 0; 185 IoCompleteRequest(Irp, IO_NO_INCREMENT); 186 187 return Status; 188 } 189 190 static NTSTATUS NTAPI 191 ClassPower( 192 IN PDEVICE_OBJECT DeviceObject, 193 IN PIRP Irp) 194 { 195 NTSTATUS Status; 196 PPORT_DEVICE_EXTENSION DeviceExtension; 197 198 DeviceExtension = DeviceObject->DeviceExtension; 199 if (!DeviceExtension->Common.IsClassDO) 200 { 201 /* Forward port DO IRPs to lower device */ 202 PoStartNextPowerIrp(Irp); 203 IoSkipCurrentIrpStackLocation(Irp); 204 return PoCallDriver(DeviceExtension->LowerDevice, Irp); 205 } 206 207 switch (IoGetCurrentIrpStackLocation(Irp)->MinorFunction) 208 { 209 case IRP_MN_SET_POWER: 210 case IRP_MN_QUERY_POWER: 211 Irp->IoStatus.Status = STATUS_SUCCESS; 212 break; 213 } 214 Status = Irp->IoStatus.Status; 215 PoStartNextPowerIrp(Irp); 216 IoCompleteRequest(Irp, IO_NO_INCREMENT); 217 return Status; 218 } 219 220 static NTSTATUS 221 ReadRegistryEntries( 222 IN PUNICODE_STRING RegistryPath, 223 IN PCLASS_DRIVER_EXTENSION DriverExtension) 224 { 225 UNICODE_STRING ParametersRegistryKey; 226 RTL_QUERY_REGISTRY_TABLE Parameters[4]; 227 NTSTATUS Status; 228 229 /* HACK: We don't support multiple devices with this disabled */ 230 ULONG DefaultConnectMultiplePorts = 1; 231 ULONG DefaultDataQueueSize = 0x64; 232 PCWSTR DefaultDeviceBaseName = L"KeyboardClass"; 233 234 ParametersRegistryKey.Length = 0; 235 ParametersRegistryKey.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(UNICODE_NULL); 236 ParametersRegistryKey.Buffer = ExAllocatePoolWithTag(PagedPool, ParametersRegistryKey.MaximumLength, CLASS_TAG); 237 if (!ParametersRegistryKey.Buffer) 238 { 239 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n"); 240 return STATUS_NO_MEMORY; 241 } 242 RtlCopyUnicodeString(&ParametersRegistryKey, RegistryPath); 243 RtlAppendUnicodeToString(&ParametersRegistryKey, L"\\Parameters"); 244 ParametersRegistryKey.Buffer[ParametersRegistryKey.Length / sizeof(WCHAR)] = UNICODE_NULL; 245 246 RtlZeroMemory(Parameters, sizeof(Parameters)); 247 248 Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; 249 Parameters[0].Name = L"ConnectMultiplePorts"; 250 Parameters[0].EntryContext = &DriverExtension->ConnectMultiplePorts; 251 Parameters[0].DefaultType = REG_DWORD; 252 Parameters[0].DefaultData = &DefaultConnectMultiplePorts; 253 Parameters[0].DefaultLength = sizeof(ULONG); 254 255 Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; 256 Parameters[1].Name = L"KeyboardDataQueueSize"; 257 Parameters[1].EntryContext = &DriverExtension->DataQueueSize; 258 Parameters[1].DefaultType = REG_DWORD; 259 Parameters[1].DefaultData = &DefaultDataQueueSize; 260 Parameters[1].DefaultLength = sizeof(ULONG); 261 262 Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL; 263 Parameters[2].Name = L"KeyboardDeviceBaseName"; 264 Parameters[2].EntryContext = &DriverExtension->DeviceBaseName; 265 Parameters[2].DefaultType = REG_SZ; 266 Parameters[2].DefaultData = (PVOID)DefaultDeviceBaseName; 267 Parameters[2].DefaultLength = 0; 268 269 Status = RtlQueryRegistryValues( 270 RTL_REGISTRY_ABSOLUTE, 271 ParametersRegistryKey.Buffer, 272 Parameters, 273 NULL, 274 NULL); 275 276 if (NT_SUCCESS(Status)) 277 { 278 /* Check values */ 279 if (DriverExtension->ConnectMultiplePorts != 0 280 && DriverExtension->ConnectMultiplePorts != 1) 281 { 282 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts; 283 } 284 if (DriverExtension->DataQueueSize == 0) 285 { 286 DriverExtension->DataQueueSize = DefaultDataQueueSize; 287 } 288 } 289 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 290 { 291 /* Registry path doesn't exist. Set defaults */ 292 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts; 293 DriverExtension->DataQueueSize = DefaultDataQueueSize; 294 if (RtlCreateUnicodeString(&DriverExtension->DeviceBaseName, DefaultDeviceBaseName)) 295 Status = STATUS_SUCCESS; 296 else 297 Status = STATUS_NO_MEMORY; 298 } 299 300 ExFreePoolWithTag(ParametersRegistryKey.Buffer, CLASS_TAG); 301 return Status; 302 } 303 304 static NTSTATUS 305 CreateClassDeviceObject( 306 IN PDRIVER_OBJECT DriverObject, 307 OUT PDEVICE_OBJECT *ClassDO OPTIONAL) 308 { 309 PCLASS_DRIVER_EXTENSION DriverExtension; 310 ULONG DeviceId = 0; 311 ULONG PrefixLength; 312 UNICODE_STRING DeviceNameU; 313 PWSTR DeviceIdW = NULL; /* Pointer into DeviceNameU.Buffer */ 314 PDEVICE_OBJECT Fdo; 315 PCLASS_DEVICE_EXTENSION DeviceExtension; 316 NTSTATUS Status; 317 318 TRACE_(CLASS_NAME, "CreateClassDeviceObject(0x%p)\n", DriverObject); 319 320 /* Create new device object */ 321 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject); 322 DeviceNameU.Length = 0; 323 DeviceNameU.MaximumLength = 324 wcslen(L"\\Device\\") * sizeof(WCHAR) /* "\Device\" */ 325 + DriverExtension->DeviceBaseName.Length /* "KeyboardClass" */ 326 + 4 * sizeof(WCHAR) /* Id between 0 and 9999 */ 327 + sizeof(UNICODE_NULL); /* Final NULL char */ 328 DeviceNameU.Buffer = ExAllocatePoolWithTag(PagedPool, DeviceNameU.MaximumLength, CLASS_TAG); 329 if (!DeviceNameU.Buffer) 330 { 331 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n"); 332 return STATUS_NO_MEMORY; 333 } 334 Status = RtlAppendUnicodeToString(&DeviceNameU, L"\\Device\\"); 335 if (!NT_SUCCESS(Status)) 336 { 337 WARN_(CLASS_NAME, "RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status); 338 goto cleanup; 339 } 340 Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->DeviceBaseName); 341 if (!NT_SUCCESS(Status)) 342 { 343 WARN_(CLASS_NAME, "RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status); 344 goto cleanup; 345 } 346 PrefixLength = DeviceNameU.MaximumLength - 4 * sizeof(WCHAR) - sizeof(UNICODE_NULL); 347 DeviceIdW = &DeviceNameU.Buffer[PrefixLength / sizeof(WCHAR)]; 348 while (DeviceId < 9999) 349 { 350 DeviceNameU.Length = (USHORT)(PrefixLength + swprintf(DeviceIdW, L"%lu", DeviceId) * sizeof(WCHAR)); 351 Status = IoCreateDevice( 352 DriverObject, 353 sizeof(CLASS_DEVICE_EXTENSION), 354 &DeviceNameU, 355 FILE_DEVICE_KEYBOARD, 356 FILE_DEVICE_SECURE_OPEN, 357 FALSE, 358 &Fdo); 359 if (NT_SUCCESS(Status)) 360 goto cleanup; 361 else if (Status != STATUS_OBJECT_NAME_COLLISION) 362 { 363 WARN_(CLASS_NAME, "IoCreateDevice() failed with status 0x%08lx\n", Status); 364 goto cleanup; 365 } 366 DeviceId++; 367 } 368 WARN_(CLASS_NAME, "Too many devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName); 369 Status = STATUS_TOO_MANY_NAMES; 370 cleanup: 371 if (!NT_SUCCESS(Status)) 372 { 373 ExFreePoolWithTag(DeviceNameU.Buffer, CLASS_TAG); 374 return Status; 375 } 376 377 DeviceExtension = (PCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension; 378 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION)); 379 DeviceExtension->Common.IsClassDO = TRUE; 380 DeviceExtension->DriverExtension = DriverExtension; 381 InitializeListHead(&DeviceExtension->ListHead); 382 KeInitializeSpinLock(&DeviceExtension->ListSpinLock); 383 KeInitializeSpinLock(&DeviceExtension->SpinLock); 384 DeviceExtension->InputCount = 0; 385 DeviceExtension->PortData = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(KEYBOARD_INPUT_DATA), CLASS_TAG); 386 if (!DeviceExtension->PortData) 387 { 388 ExFreePoolWithTag(DeviceNameU.Buffer, CLASS_TAG); 389 return STATUS_NO_MEMORY; 390 } 391 DeviceExtension->DeviceName = DeviceNameU.Buffer; 392 Fdo->Flags |= DO_POWER_PAGABLE; 393 Fdo->Flags |= DO_BUFFERED_IO; /* FIXME: Why is it needed for 1st stage setup? */ 394 Fdo->Flags &= ~DO_DEVICE_INITIALIZING; 395 396 /* Add entry entry to HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */ 397 RtlWriteRegistryValue( 398 RTL_REGISTRY_DEVICEMAP, 399 DriverExtension->DeviceBaseName.Buffer, 400 DeviceExtension->DeviceName, 401 REG_SZ, 402 DriverExtension->RegistryPath.Buffer, 403 DriverExtension->RegistryPath.MaximumLength); 404 405 if (ClassDO) 406 *ClassDO = Fdo; 407 408 return STATUS_SUCCESS; 409 } 410 411 static NTSTATUS 412 FillEntries( 413 IN PDEVICE_OBJECT ClassDeviceObject, 414 IN PIRP Irp, 415 IN PKEYBOARD_INPUT_DATA DataStart, 416 IN SIZE_T NumberOfEntries) 417 { 418 NTSTATUS Status = STATUS_SUCCESS; 419 420 if (ClassDeviceObject->Flags & DO_BUFFERED_IO) 421 { 422 RtlCopyMemory( 423 Irp->AssociatedIrp.SystemBuffer, 424 DataStart, 425 NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA)); 426 } 427 else if (ClassDeviceObject->Flags & DO_DIRECT_IO) 428 { 429 PVOID DestAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 430 if (DestAddress) 431 { 432 RtlCopyMemory( 433 DestAddress, 434 DataStart, 435 NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA)); 436 } 437 else 438 Status = STATUS_UNSUCCESSFUL; 439 } 440 else 441 { 442 _SEH2_TRY 443 { 444 RtlCopyMemory( 445 Irp->UserBuffer, 446 DataStart, 447 NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA)); 448 } 449 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 450 { 451 Status = _SEH2_GetExceptionCode(); 452 } 453 _SEH2_END; 454 } 455 456 return Status; 457 } 458 459 static BOOLEAN NTAPI 460 ClassCallback( 461 IN PDEVICE_OBJECT ClassDeviceObject, 462 IN OUT PKEYBOARD_INPUT_DATA DataStart, 463 IN PKEYBOARD_INPUT_DATA DataEnd, 464 IN OUT PULONG ConsumedCount) 465 { 466 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension; 467 KIRQL OldIrql; 468 SIZE_T InputCount = DataEnd - DataStart; 469 SIZE_T ReadSize; 470 471 TRACE_(CLASS_NAME, "ClassCallback()\n"); 472 473 ASSERT(ClassDeviceExtension->Common.IsClassDO); 474 475 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql); 476 if (InputCount > 0) 477 { 478 if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize) 479 { 480 /* 481 * We're exceeding the buffer, and data will be thrown away... 482 * FIXME: What could we do, as we are at DISPATCH_LEVEL? 483 */ 484 ReadSize = ClassDeviceExtension->DriverExtension->DataQueueSize - ClassDeviceExtension->InputCount; 485 } 486 else 487 ReadSize = InputCount; 488 489 /* 490 * Move the input data from the port data queue to our class data 491 * queue. 492 */ 493 RtlCopyMemory( 494 &ClassDeviceExtension->PortData[ClassDeviceExtension->InputCount], 495 (PCHAR)DataStart, 496 sizeof(KEYBOARD_INPUT_DATA) * ReadSize); 497 498 /* Move the counter up */ 499 ClassDeviceExtension->InputCount += ReadSize; 500 501 (*ConsumedCount) += (ULONG)ReadSize; 502 503 /* Complete pending IRP (if any) */ 504 if (ClassDeviceExtension->PendingIrp) 505 HandleReadIrp(ClassDeviceObject, ClassDeviceExtension->PendingIrp, FALSE); 506 } 507 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql); 508 509 TRACE_(CLASS_NAME, "Leaving ClassCallback()\n"); 510 return TRUE; 511 } 512 513 /* Send IOCTL_INTERNAL_*_CONNECT to port */ 514 static NTSTATUS 515 ConnectPortDriver( 516 IN PDEVICE_OBJECT PortDO, 517 IN PDEVICE_OBJECT ClassDO) 518 { 519 KEVENT Event; 520 PIRP Irp; 521 IO_STATUS_BLOCK IoStatus; 522 CONNECT_DATA ConnectData; 523 NTSTATUS Status; 524 525 TRACE_(CLASS_NAME, "Connecting PortDO %p to ClassDO %p\n", PortDO, ClassDO); 526 527 KeInitializeEvent(&Event, NotificationEvent, FALSE); 528 529 ConnectData.ClassDeviceObject = ClassDO; 530 ConnectData.ClassService = ClassCallback; 531 532 Irp = IoBuildDeviceIoControlRequest( 533 IOCTL_INTERNAL_KEYBOARD_CONNECT, 534 PortDO, 535 &ConnectData, sizeof(CONNECT_DATA), 536 NULL, 0, 537 TRUE, &Event, &IoStatus); 538 if (!Irp) 539 return STATUS_INSUFFICIENT_RESOURCES; 540 541 Status = IoCallDriver(PortDO, Irp); 542 543 if (Status == STATUS_PENDING) 544 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); 545 else 546 IoStatus.Status = Status; 547 548 if (NT_SUCCESS(IoStatus.Status)) 549 { 550 ObReferenceObject(PortDO); 551 ExInterlockedInsertTailList( 552 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListHead, 553 &((PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension)->ListEntry, 554 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListSpinLock); 555 if (ClassDO->StackSize <= PortDO->StackSize) 556 { 557 /* Increase the stack size, in case we have to 558 * forward some IRPs to the port device object 559 */ 560 ClassDO->StackSize = PortDO->StackSize + 1; 561 } 562 } 563 564 return IoStatus.Status; 565 } 566 567 /* Send IOCTL_INTERNAL_*_DISCONNECT to port + destroy the Port DO */ 568 static VOID 569 DestroyPortDriver( 570 IN PDEVICE_OBJECT PortDO) 571 { 572 PPORT_DEVICE_EXTENSION DeviceExtension; 573 PCLASS_DEVICE_EXTENSION ClassDeviceExtension; 574 PCLASS_DRIVER_EXTENSION DriverExtension; 575 KEVENT Event; 576 PIRP Irp; 577 IO_STATUS_BLOCK IoStatus; 578 KIRQL OldIrql; 579 NTSTATUS Status; 580 581 TRACE_(CLASS_NAME, "Destroying PortDO %p\n", PortDO); 582 583 DeviceExtension = (PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension; 584 ClassDeviceExtension = DeviceExtension->ClassDO->DeviceExtension; 585 DriverExtension = IoGetDriverObjectExtension(PortDO->DriverObject, PortDO->DriverObject); 586 587 /* Send IOCTL_INTERNAL_*_DISCONNECT */ 588 KeInitializeEvent(&Event, NotificationEvent, FALSE); 589 Irp = IoBuildDeviceIoControlRequest( 590 IOCTL_INTERNAL_KEYBOARD_DISCONNECT, 591 PortDO, 592 NULL, 0, 593 NULL, 0, 594 TRUE, &Event, &IoStatus); 595 if (Irp) 596 { 597 Status = IoCallDriver(PortDO, Irp); 598 if (Status == STATUS_PENDING) 599 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL); 600 } 601 602 /* Remove from ClassDeviceExtension->ListHead list */ 603 KeAcquireSpinLock(&ClassDeviceExtension->ListSpinLock, &OldIrql); 604 RemoveEntryList(&DeviceExtension->ListEntry); 605 KeReleaseSpinLock(&ClassDeviceExtension->ListSpinLock, OldIrql); 606 607 /* Remove entry from HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */ 608 RtlDeleteRegistryValue( 609 RTL_REGISTRY_DEVICEMAP, 610 DriverExtension->DeviceBaseName.Buffer, 611 ClassDeviceExtension->DeviceName); 612 613 if (DeviceExtension->LowerDevice) 614 IoDetachDevice(DeviceExtension->LowerDevice); 615 ObDereferenceObject(PortDO); 616 617 if (!DriverExtension->ConnectMultiplePorts && DeviceExtension->ClassDO) 618 { 619 ExFreePoolWithTag(ClassDeviceExtension->PortData, CLASS_TAG); 620 ExFreePoolWithTag((PVOID)ClassDeviceExtension->DeviceName, CLASS_TAG); 621 IoDeleteDevice(DeviceExtension->ClassDO); 622 } 623 624 IoDeleteDevice(PortDO); 625 } 626 627 static NTSTATUS NTAPI 628 ClassAddDevice( 629 IN PDRIVER_OBJECT DriverObject, 630 IN PDEVICE_OBJECT Pdo) 631 { 632 PCLASS_DRIVER_EXTENSION DriverExtension; 633 PDEVICE_OBJECT Fdo = NULL; 634 PPORT_DEVICE_EXTENSION DeviceExtension = NULL; 635 NTSTATUS Status; 636 637 TRACE_(CLASS_NAME, "ClassAddDevice called. Pdo = 0x%p\n", Pdo); 638 639 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject); 640 641 if (Pdo == NULL) 642 /* We may get a NULL Pdo at the first call as we're a legacy driver. Ignore it */ 643 return STATUS_SUCCESS; 644 645 /* Create new device object */ 646 Status = IoCreateDevice( 647 DriverObject, 648 sizeof(PORT_DEVICE_EXTENSION), 649 NULL, 650 Pdo->DeviceType, 651 Pdo->Characteristics & FILE_DEVICE_SECURE_OPEN ? FILE_DEVICE_SECURE_OPEN : 0, 652 FALSE, 653 &Fdo); 654 if (!NT_SUCCESS(Status)) 655 { 656 WARN_(CLASS_NAME, "IoCreateDevice() failed with status 0x%08lx\n", Status); 657 goto cleanup; 658 } 659 IoSetStartIoAttributes(Fdo, TRUE, TRUE); 660 661 DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension; 662 RtlZeroMemory(DeviceExtension, sizeof(PORT_DEVICE_EXTENSION)); 663 DeviceExtension->Common.IsClassDO = FALSE; 664 DeviceExtension->DeviceObject = Fdo; 665 DeviceExtension->PnpState = dsStopped; 666 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice); 667 if (!NT_SUCCESS(Status)) 668 { 669 WARN_(CLASS_NAME, "IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status); 670 goto cleanup; 671 } 672 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE) 673 Fdo->Flags |= DO_POWER_PAGABLE; 674 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO) 675 Fdo->Flags |= DO_BUFFERED_IO; 676 if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO) 677 Fdo->Flags |= DO_DIRECT_IO; 678 679 if (DriverExtension->ConnectMultiplePorts) 680 DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject; 681 else 682 { 683 /* We need a new class device object for this Fdo */ 684 Status = CreateClassDeviceObject( 685 DriverObject, 686 &DeviceExtension->ClassDO); 687 if (!NT_SUCCESS(Status)) 688 { 689 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status); 690 goto cleanup; 691 } 692 } 693 Status = ConnectPortDriver(Fdo, DeviceExtension->ClassDO); 694 if (!NT_SUCCESS(Status)) 695 { 696 WARN_(CLASS_NAME, "ConnectPortDriver() failed with status 0x%08lx\n", Status); 697 goto cleanup; 698 } 699 Fdo->Flags &= ~DO_DEVICE_INITIALIZING; 700 701 /* Register interface ; ignore the error (if any) as having 702 * a registered interface is not so important... */ 703 Status = IoRegisterDeviceInterface( 704 Pdo, 705 &GUID_DEVINTERFACE_KEYBOARD, 706 NULL, 707 &DeviceExtension->InterfaceName); 708 if (!NT_SUCCESS(Status)) 709 DeviceExtension->InterfaceName.Length = 0; 710 711 return STATUS_SUCCESS; 712 713 cleanup: 714 if (Fdo) 715 DestroyPortDriver(Fdo); 716 return Status; 717 } 718 719 static VOID NTAPI 720 ClassCancelRoutine( 721 IN PDEVICE_OBJECT DeviceObject, 722 IN PIRP Irp) 723 { 724 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = DeviceObject->DeviceExtension; 725 KIRQL OldIrql; 726 BOOLEAN wasQueued = FALSE; 727 728 TRACE_(CLASS_NAME, "ClassCancelRoutine(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); 729 730 ASSERT(ClassDeviceExtension->Common.IsClassDO); 731 732 IoReleaseCancelSpinLock(Irp->CancelIrql); 733 734 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql); 735 736 if (ClassDeviceExtension->PendingIrp == Irp) 737 { 738 ClassDeviceExtension->PendingIrp = NULL; 739 wasQueued = TRUE; 740 } 741 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql); 742 743 if (wasQueued) 744 { 745 Irp->IoStatus.Status = STATUS_CANCELLED; 746 Irp->IoStatus.Information = 0; 747 IoCompleteRequest(Irp, IO_NO_INCREMENT); 748 } 749 else 750 { 751 DPRINT1("Cancelled IRP is not pending. Race condition?\n"); 752 } 753 } 754 755 static NTSTATUS 756 HandleReadIrp( 757 IN PDEVICE_OBJECT DeviceObject, 758 IN PIRP Irp, 759 BOOLEAN IsInStartIo) 760 { 761 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 762 NTSTATUS Status; 763 KIRQL OldIrql; 764 765 TRACE_(CLASS_NAME, "HandleReadIrp(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); 766 767 ASSERT(DeviceExtension->Common.IsClassDO); 768 769 if (DeviceExtension->InputCount > 0) 770 { 771 SIZE_T NumberOfEntries; 772 773 NumberOfEntries = MIN( 774 DeviceExtension->InputCount, 775 IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length / sizeof(KEYBOARD_INPUT_DATA)); 776 777 Status = FillEntries( 778 DeviceObject, 779 Irp, 780 DeviceExtension->PortData, 781 NumberOfEntries); 782 783 if (NT_SUCCESS(Status)) 784 { 785 if (DeviceExtension->InputCount > NumberOfEntries) 786 { 787 RtlMoveMemory( 788 &DeviceExtension->PortData[0], 789 &DeviceExtension->PortData[NumberOfEntries], 790 (DeviceExtension->InputCount - NumberOfEntries) * sizeof(KEYBOARD_INPUT_DATA)); 791 } 792 793 DeviceExtension->InputCount -= NumberOfEntries; 794 795 Irp->IoStatus.Information = NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA); 796 } 797 798 /* Go to next packet and complete this request */ 799 Irp->IoStatus.Status = Status; 800 801 (VOID)IoSetCancelRoutine(Irp, NULL); 802 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT); 803 DeviceExtension->PendingIrp = NULL; 804 } 805 else 806 { 807 IoAcquireCancelSpinLock(&OldIrql); 808 if (Irp->Cancel) 809 { 810 DeviceExtension->PendingIrp = NULL; 811 Status = STATUS_CANCELLED; 812 } 813 else 814 { 815 IoMarkIrpPending(Irp); 816 DeviceExtension->PendingIrp = Irp; 817 (VOID)IoSetCancelRoutine(Irp, ClassCancelRoutine); 818 Status = STATUS_PENDING; 819 } 820 IoReleaseCancelSpinLock(OldIrql); 821 } 822 return Status; 823 } 824 825 static NTSTATUS NTAPI 826 ClassPnp( 827 IN PDEVICE_OBJECT DeviceObject, 828 IN PIRP Irp) 829 { 830 PPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 831 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 832 OBJECT_ATTRIBUTES ObjectAttributes; 833 IO_STATUS_BLOCK Iosb; 834 NTSTATUS Status; 835 836 switch (IrpSp->MinorFunction) 837 { 838 case IRP_MN_START_DEVICE: 839 Status = ForwardIrpAndWait(DeviceObject, Irp); 840 if (NT_SUCCESS(Status)) 841 { 842 InitializeObjectAttributes(&ObjectAttributes, 843 &DeviceExtension->InterfaceName, 844 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 845 NULL, 846 NULL); 847 848 Status = ZwOpenFile(&DeviceExtension->FileHandle, 849 FILE_READ_DATA, 850 &ObjectAttributes, 851 &Iosb, 852 0, 853 0); 854 if (!NT_SUCCESS(Status)) 855 DeviceExtension->FileHandle = NULL; 856 } 857 else 858 DeviceExtension->FileHandle = NULL; 859 Irp->IoStatus.Status = Status; 860 IoCompleteRequest(Irp, IO_NO_INCREMENT); 861 return Status; 862 863 case IRP_MN_STOP_DEVICE: 864 if (DeviceExtension->FileHandle) 865 { 866 ZwClose(DeviceExtension->FileHandle); 867 DeviceExtension->FileHandle = NULL; 868 } 869 Status = STATUS_SUCCESS; 870 break; 871 872 case IRP_MN_REMOVE_DEVICE: 873 if (DeviceExtension->FileHandle) 874 { 875 ZwClose(DeviceExtension->FileHandle); 876 DeviceExtension->FileHandle = NULL; 877 } 878 IoSkipCurrentIrpStackLocation(Irp); 879 Status = IoCallDriver(DeviceExtension->LowerDevice, Irp); 880 DestroyPortDriver(DeviceObject); 881 return Status; 882 883 default: 884 Status = Irp->IoStatus.Status; 885 break; 886 } 887 888 Irp->IoStatus.Status = Status; 889 if (NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED) 890 { 891 IoSkipCurrentIrpStackLocation(Irp); 892 return IoCallDriver(DeviceExtension->LowerDevice, Irp); 893 } 894 else 895 { 896 IoCompleteRequest(Irp, IO_NO_INCREMENT); 897 return Status; 898 } 899 } 900 901 static VOID NTAPI 902 ClassStartIo( 903 IN PDEVICE_OBJECT DeviceObject, 904 IN PIRP Irp) 905 { 906 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 907 KIRQL OldIrql; 908 909 TRACE_(CLASS_NAME, "ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp); 910 911 ASSERT(DeviceExtension->Common.IsClassDO); 912 913 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql); 914 HandleReadIrp(DeviceObject, Irp, TRUE); 915 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql); 916 } 917 918 static VOID NTAPI 919 SearchForLegacyDrivers( 920 IN PDRIVER_OBJECT DriverObject, 921 IN PVOID Context, /* PCLASS_DRIVER_EXTENSION */ 922 IN ULONG Count) 923 { 924 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP"); 925 PCLASS_DRIVER_EXTENSION DriverExtension; 926 UNICODE_STRING PortBaseName = { 0, 0, NULL }; 927 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL; 928 OBJECT_ATTRIBUTES ObjectAttributes; 929 HANDLE hDeviceMapKey = (HANDLE)-1; 930 HANDLE hPortKey = (HANDLE)-1; 931 ULONG Index = 0; 932 ULONG Size, ResultLength; 933 NTSTATUS Status; 934 935 TRACE_(CLASS_NAME, "SearchForLegacyDrivers(%p %p %lu)\n", 936 DriverObject, Context, Count); 937 938 if (Count != 1) 939 return; 940 DriverExtension = (PCLASS_DRIVER_EXTENSION)Context; 941 942 /* Create port base name, by replacing Class by Port at the end of the class base name */ 943 Status = DuplicateUnicodeString( 944 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, 945 &DriverExtension->DeviceBaseName, 946 &PortBaseName); 947 if (!NT_SUCCESS(Status)) 948 { 949 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status); 950 goto cleanup; 951 } 952 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL)); 953 RtlAppendUnicodeToString(&PortBaseName, L"Port"); 954 955 /* Allocate memory */ 956 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH; 957 KeyValueInformation = ExAllocatePoolWithTag(PagedPool, Size, CLASS_TAG); 958 if (!KeyValueInformation) 959 { 960 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n"); 961 Status = STATUS_NO_MEMORY; 962 goto cleanup; 963 } 964 965 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */ 966 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); 967 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes); 968 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 969 { 970 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP is non-existent\n"); 971 Status = STATUS_SUCCESS; 972 goto cleanup; 973 } 974 else if (!NT_SUCCESS(Status)) 975 { 976 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status); 977 goto cleanup; 978 } 979 980 /* Open sub key */ 981 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL); 982 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes); 983 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 984 { 985 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName); 986 Status = STATUS_SUCCESS; 987 goto cleanup; 988 } 989 else if (!NT_SUCCESS(Status)) 990 { 991 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status); 992 goto cleanup; 993 } 994 995 /* Read each value name */ 996 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS) 997 { 998 UNICODE_STRING PortName; 999 PDEVICE_OBJECT PortDeviceObject = NULL; 1000 PFILE_OBJECT FileObject = NULL; 1001 1002 PortName.Length = PortName.MaximumLength = (USHORT)KeyValueInformation->NameLength; 1003 PortName.Buffer = KeyValueInformation->Name; 1004 1005 /* Open the device object pointer */ 1006 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject); 1007 if (!NT_SUCCESS(Status)) 1008 { 1009 WARN_(CLASS_NAME, "IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", &PortName, Status); 1010 continue; 1011 } 1012 INFO_(CLASS_NAME, "Legacy driver found\n"); 1013 1014 Status = ClassAddDevice(DriverObject, PortDeviceObject); 1015 if (!NT_SUCCESS(Status)) 1016 { 1017 /* FIXME: Log the error */ 1018 WARN_(CLASS_NAME, "ClassAddDevice() failed with status 0x%08lx\n", Status); 1019 } 1020 1021 ObDereferenceObject(FileObject); 1022 } 1023 1024 cleanup: 1025 if (KeyValueInformation != NULL) 1026 ExFreePoolWithTag(KeyValueInformation, CLASS_TAG); 1027 if (hDeviceMapKey != (HANDLE)-1) 1028 ZwClose(hDeviceMapKey); 1029 if (hPortKey != (HANDLE)-1) 1030 ZwClose(hPortKey); 1031 } 1032 1033 /* 1034 * Standard DriverEntry method. 1035 */ 1036 NTSTATUS NTAPI 1037 DriverEntry( 1038 IN PDRIVER_OBJECT DriverObject, 1039 IN PUNICODE_STRING RegistryPath) 1040 { 1041 PCLASS_DRIVER_EXTENSION DriverExtension; 1042 NTSTATUS Status; 1043 1044 Status = IoAllocateDriverObjectExtension( 1045 DriverObject, 1046 DriverObject, 1047 sizeof(CLASS_DRIVER_EXTENSION), 1048 (PVOID*)&DriverExtension); 1049 if (!NT_SUCCESS(Status)) 1050 { 1051 WARN_(CLASS_NAME, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status); 1052 return Status; 1053 } 1054 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION)); 1055 1056 Status = DuplicateUnicodeString( 1057 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, 1058 RegistryPath, 1059 &DriverExtension->RegistryPath); 1060 if (!NT_SUCCESS(Status)) 1061 { 1062 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status); 1063 return Status; 1064 } 1065 1066 Status = ReadRegistryEntries(RegistryPath, DriverExtension); 1067 if (!NT_SUCCESS(Status)) 1068 { 1069 WARN_(CLASS_NAME, "ReadRegistryEntries() failed with status 0x%08lx\n", Status); 1070 return Status; 1071 } 1072 1073 if (DriverExtension->ConnectMultiplePorts == 1) 1074 { 1075 Status = CreateClassDeviceObject( 1076 DriverObject, 1077 &DriverExtension->MainClassDeviceObject); 1078 if (!NT_SUCCESS(Status)) 1079 { 1080 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status); 1081 return Status; 1082 } 1083 } 1084 1085 DriverObject->DriverExtension->AddDevice = ClassAddDevice; 1086 DriverObject->DriverUnload = DriverUnload; 1087 1088 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate; 1089 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose; 1090 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup; 1091 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead; 1092 DriverObject->MajorFunction[IRP_MJ_POWER] = ClassPower; 1093 DriverObject->MajorFunction[IRP_MJ_PNP] = ClassPnp; 1094 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl; 1095 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget; 1096 DriverObject->DriverStartIo = ClassStartIo; 1097 1098 /* We will detect the legacy devices later */ 1099 IoRegisterDriverReinitialization( 1100 DriverObject, 1101 SearchForLegacyDrivers, 1102 DriverExtension); 1103 1104 return STATUS_SUCCESS; 1105 } 1106