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.

713 lines
23 KiB

  1. /*
  2. ********************************************************************************
  3. *
  4. * VXDCLNT.C
  5. *
  6. *
  7. * VXDCLNT - Sample Ring-0 HID device mapper for Memphis
  8. *
  9. * Copyright 1997 Microsoft Corp.
  10. *
  11. * (ep)
  12. *
  13. ********************************************************************************
  14. */
  15. #define INITGUID
  16. #include "vxdclnt.h"
  17. deviceContext *firstDevice = NULL, *lastDevice = NULL;
  18. VMM_SEMAPHORE shutdownSemaphore = (VMM_SEMAPHORE)NULL;
  19. BOOL ShutDown = FALSE;
  20. /*
  21. * Import function pointers
  22. */
  23. t_pHidP_GetUsageValue pHidP_GetUsageValue = NULL;
  24. t_pHidP_GetScaledUsageValue pHidP_GetScaledUsageValue = NULL;
  25. t_pHidP_SetUsages pHidP_SetUsages = NULL;
  26. t_pHidP_GetUsages pHidP_GetUsages = NULL;
  27. t_pHidP_MaxUsageListLength pHidP_MaxUsageListLength = NULL;
  28. t_pIoGetDeviceClassAssociations pIoGetDeviceClassAssociations = NULL;
  29. t_pHidP_GetCaps pHidP_GetCaps = NULL;
  30. t_pHidP_GetValueCaps pHidP_GetValueCaps = NULL;
  31. #ifdef DEBUG
  32. UINT dbgOpt = 0;
  33. #endif
  34. /*
  35. * GetImportFunctionPtrs
  36. *
  37. * Set global pointers to imported functions from HIDPARSE and NTKERN.
  38. */
  39. BOOL GetImportFunctionPtrs()
  40. {
  41. static BOOL haveAllPtrs = FALSE;
  42. if (!haveAllPtrs){
  43. pHidP_GetUsageValue = (t_pHidP_GetUsageValue)
  44. _PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetUsageValue", NULL);
  45. pHidP_GetScaledUsageValue = (t_pHidP_GetScaledUsageValue)
  46. _PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetScaledUsageValue", NULL);
  47. pHidP_GetUsages = (t_pHidP_GetUsages)
  48. _PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetUsages", NULL);
  49. pHidP_SetUsages = (t_pHidP_SetUsages)
  50. _PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_SetUsages", NULL);
  51. pHidP_MaxUsageListLength = (t_pHidP_MaxUsageListLength)
  52. _PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_MaxUsageListLength", NULL);
  53. pIoGetDeviceClassAssociations = (t_pIoGetDeviceClassAssociations)
  54. _PELDR_GetProcAddress((struct HPEMODULE__ *)"ntpnp.sys", "IoGetDeviceClassAssociations", NULL);
  55. pHidP_GetCaps = (t_pHidP_GetCaps)
  56. _PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetCaps", NULL);
  57. pHidP_GetValueCaps = (t_pHidP_GetValueCaps)
  58. _PELDR_GetProcAddress((struct HPEMODULE__ *)"hidparse.sys", "HidP_GetValueCaps", NULL);
  59. if ( pHidP_GetUsageValue &&
  60. pHidP_GetScaledUsageValue &&
  61. pHidP_GetUsages &&
  62. pHidP_SetUsages &&
  63. pHidP_MaxUsageListLength &&
  64. pIoGetDeviceClassAssociations &&
  65. pHidP_GetCaps &&
  66. pHidP_GetValueCaps){
  67. haveAllPtrs = TRUE;
  68. }
  69. }
  70. return haveAllPtrs;
  71. }
  72. /*
  73. * WStrLen
  74. *
  75. */
  76. ULONG WStrLen(PWCHAR str)
  77. {
  78. ULONG result = 0;
  79. while (*str++){
  80. result++;
  81. }
  82. return result;
  83. }
  84. /*
  85. * NewDevice
  86. *
  87. *
  88. */
  89. deviceContext *NewDevice( HANDLE devHandle,
  90. PHIDP_CAPS caps,
  91. PHIDP_PREPARSED_DATA desc,
  92. UINT descSize,
  93. PWCHAR deviceFileName)
  94. {
  95. deviceContext *newDevice;
  96. DBGOUT(("NewDevice()"));
  97. newDevice = (deviceContext *)_HeapAllocate(sizeof(deviceContext), 0);
  98. if (newDevice){
  99. NTSTATUS ntstat;
  100. ULONG valueCapsLen;
  101. DBGOUT(("Allocated new device @ %xh ", (UINT)newDevice));
  102. RtlZeroMemory(newDevice, sizeof(deviceContext));
  103. newDevice->devHandle = devHandle;
  104. newDevice->readPending = FALSE;
  105. newDevice->next = NULL;
  106. RtlCopyMemory( (PVOID)&newDevice->deviceFileName,
  107. (PVOID)deviceFileName,
  108. (WStrLen(deviceFileName)*sizeof(WCHAR))+sizeof(UNICODE_NULL));
  109. RtlCopyMemory((PVOID)&newDevice->hidCapabilities, (PVOID)caps, sizeof(HIDP_CAPS));
  110. ExInitializeWorkItem(&newDevice->workItemRead, WorkItemCallback_Read, newDevice);
  111. ExInitializeWorkItem(&newDevice->workItemWrite, WorkItemCallback_Write, newDevice);
  112. /*
  113. * Allocate space for the device descriptor.
  114. */
  115. newDevice->hidDescriptor = (PHIDP_PREPARSED_DATA)_HeapAllocate(descSize, 0);
  116. if (newDevice->hidDescriptor){
  117. RtlCopyMemory((PVOID)newDevice->hidDescriptor, (PVOID)desc, descSize);
  118. }
  119. else {
  120. DBGERR(("_HeapAllocate for HID descriptor failed in NewDevice()"));
  121. goto _deviceInitError;
  122. }
  123. newDevice->writeReportQueueSemaphore = Create_Semaphore(0);
  124. if (!newDevice->writeReportQueueSemaphore){
  125. goto _deviceInitError;
  126. }
  127. /*
  128. * Allocate space for the device report.
  129. */
  130. newDevice->report = (PUCHAR)_HeapAllocate(newDevice->hidCapabilities.InputReportByteLength, 0);
  131. if (!newDevice->report){
  132. DBGERR(("_HeapAllocate for report buffer failed in NewDevice()"));
  133. goto _deviceInitError;
  134. }
  135. /*
  136. * Figure out the length of the buttons value
  137. * and allocate a buffer for reading the buttons.
  138. */
  139. newDevice->buttonListLength = pHidP_MaxUsageListLength(HidP_Input, HID_USAGE_PAGE_BUTTON, newDevice->hidDescriptor);
  140. DBGOUT(("Button values list length = %d.", newDevice->buttonListLength));
  141. if (newDevice->buttonListLength){
  142. newDevice->buttonValues = (PUSAGE) _HeapAllocate(newDevice->buttonListLength * sizeof (USAGE), 0);
  143. if (newDevice->buttonValues){
  144. RtlZeroMemory(newDevice->buttonValues, newDevice->buttonListLength);
  145. }
  146. else {
  147. DBGERR(("HeapAlloc failed for button values buffer"));
  148. goto _deviceInitError;
  149. }
  150. }
  151. /*
  152. * Allocate the array of value-caps.
  153. */
  154. valueCapsLen = caps->NumberInputValueCaps;
  155. if (valueCapsLen){
  156. newDevice->valueCaps = (PHIDP_VALUE_CAPS)_HeapAllocate(valueCapsLen*sizeof(HIDP_VALUE_CAPS), 0);
  157. if (!newDevice->valueCaps){
  158. DBGERR(("HeapAlloc failed for value caps"));
  159. goto _deviceInitError;
  160. }
  161. ntstat = pHidP_GetValueCaps(HidP_Input, newDevice->valueCaps, &valueCapsLen, desc);
  162. if (NT_SUCCESS(ntstat)){
  163. /*
  164. * Read valueCaps structure for information about the types of
  165. * values returned by the device.
  166. */
  167. }
  168. else {
  169. DBGERR(("HidP_GetValueCaps failed with %xh", ntstat));
  170. goto _deviceInitError;
  171. }
  172. }
  173. else {
  174. DBGERR(("value caps length = 0!"));
  175. goto _deviceInitError;
  176. }
  177. }
  178. else {
  179. DBGERR(("_HeapAllocate failed in NewDevice()"));
  180. goto _deviceInitError;
  181. }
  182. return newDevice;
  183. _deviceInitError:
  184. if (newDevice){
  185. if (newDevice->hidDescriptor){
  186. _HeapFree(newDevice->hidDescriptor, 0);
  187. }
  188. if (newDevice->report){
  189. _HeapFree(newDevice->report, 0);
  190. }
  191. if (newDevice->buttonValues){
  192. _HeapFree(newDevice->buttonValues, 0);
  193. }
  194. if (newDevice->valueCaps){
  195. _HeapFree(newDevice->valueCaps, 0);
  196. }
  197. _HeapFree(newDevice, 0);
  198. }
  199. return NULL;
  200. }
  201. /*
  202. * EnqueueDevice
  203. *
  204. */
  205. VOID EnqueueDevice(deviceContext *device)
  206. {
  207. if (lastDevice){
  208. lastDevice->next = device;
  209. lastDevice = device;
  210. }
  211. else {
  212. firstDevice = lastDevice = device;
  213. }
  214. device->next = NULL;
  215. }
  216. /*
  217. * DequeueDevice
  218. *
  219. */
  220. VOID DequeueDevice(deviceContext *device)
  221. {
  222. deviceContext *prevDevice, *thisDevice;
  223. thisDevice = firstDevice;
  224. prevDevice = NULL;
  225. while (thisDevice){
  226. if (thisDevice == device){
  227. if (prevDevice){
  228. prevDevice->next = thisDevice->next;
  229. if (!thisDevice->next){
  230. lastDevice = prevDevice;
  231. }
  232. }
  233. else {
  234. if (thisDevice->next){
  235. firstDevice = thisDevice->next;
  236. }
  237. else {
  238. firstDevice = lastDevice = NULL;
  239. }
  240. }
  241. thisDevice->next = NULL;
  242. break;
  243. }
  244. else {
  245. prevDevice = thisDevice;
  246. thisDevice = thisDevice->next;
  247. }
  248. }
  249. }
  250. /*
  251. * DestroyDevice
  252. *
  253. * Destroy the device context.
  254. * This function assumes the device context has already been dequeued
  255. * from the global list headed by firstDevice.
  256. *
  257. */
  258. VOID DestroyDevice(deviceContext *device)
  259. {
  260. DBGOUT(("==> DestroyDevice()"));
  261. /*
  262. * Modify the device's internal workItem to do a close instead of a read.
  263. * Then queue the work item so that NtClose is called on a worker thread.
  264. */
  265. ExInitializeWorkItem(&device->workItemClose, WorkItemCallback_Close, device);
  266. _NtKernQueueWorkItem(&device->workItemClose, DelayedWorkQueue);
  267. DBGOUT(("<== DestroyDevice()"));
  268. }
  269. /*
  270. * WorkItemCallback_Close
  271. *
  272. */
  273. VOID WorkItemCallback_Close(PVOID context)
  274. {
  275. deviceContext *device = (deviceContext *)context;
  276. DBGOUT(("==> WorkItemCallback_Close()"));
  277. _NtKernClose(device->devHandle);
  278. if (device->hidDescriptor){
  279. _HeapFree(device->hidDescriptor, 0);
  280. }
  281. if (device->report){
  282. _HeapFree(device->report, 0);
  283. }
  284. if (device->buttonValues){
  285. _HeapFree(device->buttonValues, 0);
  286. }
  287. if (device->valueCaps){
  288. _HeapFree(device->valueCaps, 0);
  289. }
  290. if (device->writeReportQueueSemaphore){
  291. Destroy_Semaphore(device->writeReportQueueSemaphore);
  292. }
  293. _HeapFree(device, 0);
  294. DBGOUT(("<== WorkItemCallback_Close()"));
  295. }
  296. /*
  297. * TryDestroyAll
  298. *
  299. * Destroy all devices which don't have a pending read.
  300. */
  301. VOID TryDestroyAll()
  302. {
  303. deviceContext *thisDevice;
  304. DBGOUT(("=> TryDestroyAll()"));
  305. thisDevice = firstDevice;
  306. while (thisDevice){
  307. deviceContext *nextDevice = thisDevice->next; // hold the next ptr in case we dequeue
  308. if (!thisDevice->readPending){
  309. /*
  310. * No read pending on this device; we can shut it down.
  311. */
  312. DequeueDevice(thisDevice);
  313. DestroyDevice(thisDevice);
  314. }
  315. thisDevice = nextDevice;
  316. }
  317. if (!firstDevice){
  318. /*
  319. * All reads are complete and all devices have been destroyed.
  320. * If a shutdown is suspended, shutdown now.
  321. */
  322. if (shutdownSemaphore){
  323. Signal_Semaphore_No_Switch(shutdownSemaphore);
  324. }
  325. }
  326. DBGOUT(("<= TryDestroyAll()"));
  327. }
  328. /*
  329. * HandleShutdown
  330. *
  331. *
  332. */
  333. VOID _cdecl HandleShutdown(VOID)
  334. {
  335. /*
  336. * Just set a flag. Wait for read completion to close the device handles.
  337. */
  338. DBGOUT(("==> HandleShutdown"));
  339. TryDestroyAll();
  340. if (firstDevice && !ShutDown){
  341. /*
  342. * There are still reads pending.
  343. * Wait for all reads to complete before returning.
  344. */
  345. ShutDown = TRUE;
  346. shutdownSemaphore = Create_Semaphore(0);
  347. if (shutdownSemaphore){
  348. Wait_Semaphore(shutdownSemaphore, 0);
  349. Destroy_Semaphore(shutdownSemaphore);
  350. }
  351. }
  352. DBGOUT(("<== HandleShutdown"));
  353. }
  354. /*
  355. * HandleNewDevice
  356. *
  357. */
  358. VOID _cdecl HandleNewDevice(VOID)
  359. {
  360. DBGOUT(("==> HandleNewDevice"));
  361. /*
  362. * See if there are any new device devices to connect.
  363. */
  364. ConnectNTDeviceDrivers();
  365. DBGOUT(("<== HandleNewDevice"));
  366. }
  367. /*
  368. * DeviceHasBeenOpened
  369. *
  370. * BUGBUG - there's got to be a better way of checking for this.
  371. *
  372. */
  373. BOOL DeviceHasBeenOpened(PWCHAR deviceFileName, UINT nameWChars)
  374. {
  375. deviceContext *device;
  376. UINT nameLen = (nameWChars*sizeof(WCHAR))+sizeof(UNICODE_NULL);
  377. for (device = firstDevice; device; device = device->next){
  378. if (memcmp(deviceFileName, device->deviceFileName, nameLen) == 0){
  379. return TRUE;
  380. }
  381. }
  382. return FALSE;
  383. }
  384. /*
  385. * ConnectNTDeviceDrivers
  386. *
  387. *
  388. */
  389. VOID ConnectNTDeviceDrivers()
  390. {
  391. WORK_QUEUE_ITEM *workItemOpen;
  392. workItemOpen = _HeapAllocate(sizeof(WORK_QUEUE_ITEM), 0);
  393. if (workItemOpen){
  394. /*
  395. * Initialize the workItem and
  396. * pass the workItem itself as the context so that it can be freed later.
  397. */
  398. ExInitializeWorkItem(workItemOpen, WorkItemCallback_Open, workItemOpen);
  399. DBGOUT(("==> ConnectNTDeviceDrivers() - queueing work item to call "));
  400. /*
  401. * Queue a work item to do the open; this way we'll be on a worker thread
  402. * instead of (possibly) the NTKERN thread when we call NtCreateFile().
  403. * This prevents a contention bug.
  404. */
  405. _NtKernQueueWorkItem(workItemOpen, DelayedWorkQueue);
  406. }
  407. DBGOUT(("<== ConnectNTDeviceDrivers()"));
  408. }
  409. /*
  410. * WorkItemCallback_Open
  411. *
  412. * Do the actual work of opening the device.
  413. *
  414. */
  415. VOID WorkItemCallback_Open(PVOID context)
  416. {
  417. IO_STATUS_BLOCK IoStatusBlock;
  418. NTSTATUS ntStatus;
  419. OBJECT_ATTRIBUTES Obja;
  420. UNICODE_STRING FileName;
  421. PWSTR symbolicLinkList = NULL;
  422. PWSTR symbolicLink;
  423. DBGOUT(("==> WorkItemCallback_Open()"));
  424. /*
  425. * The context is just the workItem itself, which can now be freed.
  426. */
  427. ASSERT(context);
  428. _HeapFree(context, 0);
  429. /*
  430. * Get pointers to all our import functions at once.
  431. */
  432. if (!GetImportFunctionPtrs()){
  433. DBGERR(("ERROR: Failed to get import functions."));
  434. return;
  435. }
  436. /*
  437. * Get a multi-string (separated by unicode NULL characters)
  438. * of symbolic link names to the input-class devices.
  439. */
  440. ntStatus = pIoGetDeviceClassAssociations( (EXTERN_C GUID *)&GUID_CLASS_INPUT,
  441. NULL,
  442. 0,
  443. (PWSTR *)&symbolicLinkList);
  444. if (NT_ERROR(ntStatus) || !symbolicLinkList) {
  445. DBGERR(("pIoGetDeviceClassAssociations failed"));
  446. return;
  447. }
  448. /*
  449. * Go through all the device paths
  450. */
  451. symbolicLink = symbolicLinkList;
  452. while ((WCHAR)*symbolicLink){
  453. HANDLE deviceHandle;
  454. ULONG fileNameWChars;
  455. PWCHAR fileName;
  456. deviceContext *newDevice = NULL;
  457. /*
  458. * Get a pointer to to the next device name and step the multi-string pointer.
  459. */
  460. fileName = symbolicLink;
  461. fileNameWChars = WStrLen(fileName);
  462. symbolicLink += fileNameWChars+1;
  463. /*
  464. * Make sure we don't already have this device open.
  465. * This can happen because we check for new device on every PNP_NEW_DEVNODE msg.
  466. */
  467. if (DeviceHasBeenOpened(fileName, fileNameWChars)){
  468. DBGOUT(("This device is already open, skipping ..."));
  469. }
  470. else {
  471. FileName.Buffer = fileName;
  472. FileName.Length = fileNameWChars*sizeof(WCHAR);
  473. FileName.MaximumLength = FileName.Length + sizeof(UNICODE_NULL);
  474. /*
  475. * Initialize an object-attribute structure with this filename.
  476. */
  477. InitializeObjectAttributes(&Obja, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
  478. /*
  479. * Try to open the device.
  480. */
  481. DBGOUT(("Opening HID Device : (unicode name @%xh, %xh wchars)", (UINT)fileName, (UINT)fileNameWChars));
  482. ntStatus = _NtKernCreateFile(
  483. &deviceHandle,
  484. (GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES),
  485. &Obja,
  486. &IoStatusBlock,
  487. NULL,
  488. FILE_ATTRIBUTE_NORMAL,
  489. (FILE_SHARE_READ | FILE_SHARE_WRITE),
  490. FILE_OPEN,
  491. 0,
  492. NULL,
  493. 0
  494. );
  495. if (NT_SUCCESS(ntStatus)){
  496. HID_COLLECTION_INFORMATION hidColInfo;
  497. DBGOUT(("Opened some device (handle=%xh), calling _NtKernDeviceIoControl", (UINT)deviceHandle));
  498. ntStatus = _NtKernDeviceIoControl(
  499. deviceHandle,
  500. NULL,
  501. NULL,
  502. NULL,
  503. &IoStatusBlock,
  504. IOCTL_HID_GET_COLLECTION_INFORMATION,
  505. NULL,
  506. 0,
  507. &hidColInfo,
  508. sizeof(HID_COLLECTION_INFORMATION));
  509. if (NT_SUCCESS(ntStatus)){
  510. PHIDP_PREPARSED_DATA phidDescriptor;
  511. phidDescriptor = (PHIDP_PREPARSED_DATA)_HeapAllocate(hidColInfo.DescriptorSize, 0);
  512. if (phidDescriptor){
  513. ntStatus = _NtKernDeviceIoControl(
  514. deviceHandle,
  515. NULL,
  516. NULL,
  517. NULL,
  518. &IoStatusBlock,
  519. IOCTL_HID_GET_COLLECTION_DESCRIPTOR,
  520. NULL,
  521. 0,
  522. phidDescriptor,
  523. hidColInfo.DescriptorSize);
  524. if (NT_SUCCESS(ntStatus)){
  525. HIDP_CAPS hidCaps;
  526. ntStatus = pHidP_GetCaps(phidDescriptor, (PHIDP_CAPS)&hidCaps);
  527. if (NT_SUCCESS(ntStatus)){
  528. DBGOUT(("Opened HID device successfully, report size is %d.", (UINT)hidCaps.InputReportByteLength));
  529. /*
  530. * <<COMPLETE>>
  531. *
  532. * Check hidCaps.UsagePage and hidCaps.Usage to verify that this is a device
  533. * that you want to drive.
  534. */
  535. if (hidCaps.InputReportByteLength == 0){
  536. DBGERR(("ERROR: Report size is zero!"));
  537. }
  538. else {
  539. /*
  540. * Take all the information we have for this device and bundle
  541. * it into a context.
  542. */
  543. newDevice = NewDevice(deviceHandle,
  544. (PHIDP_CAPS)&hidCaps,
  545. phidDescriptor,
  546. hidColInfo.DescriptorSize,
  547. fileName);
  548. if (newDevice){
  549. /*
  550. * Add this device to our global list.
  551. */
  552. EnqueueDevice(newDevice);
  553. /*
  554. * Then start the first async read in the device device.
  555. */
  556. DispatchNtReadFile(newDevice);
  557. }
  558. else {
  559. DBGERR(("NewDevice() failed"));
  560. }
  561. }
  562. }
  563. else {
  564. DBGERR(("pHidP_GetCaps failed"));
  565. }
  566. }
  567. else {
  568. DBGERR(("_NtKernDeviceIoControl (#2) failed"));
  569. }
  570. _HeapFree(phidDescriptor, 0);
  571. }
  572. else {
  573. DBGERR(("HeapAlloc failed"));
  574. }
  575. }
  576. else {
  577. DBGERR(("_NtKernDeviceIoControl failed"));
  578. }
  579. if (!newDevice){
  580. DBGERR(("Device init failed -- calling _NtKernClose() on device handle"));
  581. _NtKernClose(deviceHandle);
  582. }
  583. }
  584. else {
  585. DBGERR(("_NtKernCreateFile failed to open this Device (ntStatus=%xh)", (UINT)ntStatus));
  586. }
  587. }
  588. }
  589. // BUGBUG ExFreePool(symbolicLinkList);
  590. DBGOUT(("<== WorkItemCallback_Open()"));
  591. }