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.

544 lines
20 KiB

  1. /*
  2. * DEVICES.C
  3. *
  4. * Point-of-Sale Control Panel Applet
  5. *
  6. * Author: Ervin Peretz
  7. *
  8. * (c) 2001 Microsoft Corporation
  9. */
  10. #include <windows.h>
  11. #include <windowsx.h>
  12. #include <commctrl.h>
  13. #include <cpl.h>
  14. #include <setupapi.h>
  15. #include <hidsdi.h>
  16. #include "internal.h"
  17. #include "res.h"
  18. #include "debug.h"
  19. ULONG numDeviceInstances = 0;
  20. LIST_ENTRY allPOSDevicesList;
  21. posDevice *NewPOSDevice( DWORD dialogId,
  22. HANDLE devHandle,
  23. PWCHAR devPath,
  24. PHIDP_PREPARSED_DATA pHidPreparsedData,
  25. PHIDD_ATTRIBUTES pHidAttrib,
  26. HIDP_CAPS *pHidCapabilities)
  27. {
  28. posDevice *newPosDev;
  29. newPosDev = (posDevice *)GlobalAlloc( GMEM_FIXED|GMEM_ZEROINIT,
  30. sizeof(posDevice));
  31. if (newPosDev){
  32. newPosDev->sig = POSCPL_SIG;
  33. InitializeListHead(&newPosDev->listEntry);
  34. newPosDev->dialogId = dialogId;
  35. newPosDev->devHandle = devHandle;
  36. WStrNCpy(newPosDev->pathName, devPath, MAX_PATH);
  37. newPosDev->hidPreparsedData = pHidPreparsedData;
  38. newPosDev->hidAttrib = *pHidAttrib;
  39. newPosDev->hidCapabilities = *pHidCapabilities;
  40. /*
  41. * Allocate components of the context
  42. */
  43. if (newPosDev->hidCapabilities.InputReportByteLength){
  44. newPosDev->readBuffer = GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,
  45. newPosDev->hidCapabilities.InputReportByteLength);
  46. }
  47. if (newPosDev->hidCapabilities.OutputReportByteLength){
  48. newPosDev->writeBuffer = GlobalAlloc( GMEM_FIXED|GMEM_ZEROINIT,
  49. newPosDev->hidCapabilities.OutputReportByteLength);
  50. }
  51. #if USE_OVERLAPPED_IO
  52. newPosDev->overlappedReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  53. newPosDev->overlappedWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  54. #endif
  55. /*
  56. * Check if we allocated everything successfully.
  57. */
  58. if (
  59. (newPosDev->readBuffer || !newPosDev->hidCapabilities.InputReportByteLength) &&
  60. (newPosDev->writeBuffer || !newPosDev->hidCapabilities.OutputReportByteLength) &&
  61. #if USE_OVERLAPPED_IO
  62. newPosDev->overlappedReadEvent &&
  63. newPosDev->overlappedWriteEvent &&
  64. #endif
  65. TRUE
  66. ){
  67. /*
  68. * Created device context successfully.
  69. */
  70. }
  71. else {
  72. DBGERR(L"allocation error in NewPOSDevice()", 0);
  73. DestroyPOSDevice(newPosDev);
  74. newPosDev = NULL;
  75. }
  76. }
  77. ASSERT(newPosDev);
  78. return newPosDev;
  79. }
  80. VOID DestroyPOSDevice(posDevice *posDev)
  81. {
  82. ASSERT(IsListEmpty(&posDev->listEntry));
  83. /*
  84. * Note: this destroy function is called from a failed NewPOSDevice()
  85. * call as well; so check every pointer before freeing.
  86. */
  87. if (posDev->readBuffer) GlobalFree(posDev->readBuffer);
  88. if (posDev->writeBuffer) GlobalFree(posDev->writeBuffer);
  89. if (posDev->hidPreparsedData) GlobalFree(posDev->hidPreparsedData);
  90. #if USE_OVERLAPPED_IO
  91. if (posDev->overlappedReadEvent) CloseHandle(posDev->overlappedReadEvent);
  92. if (posDev->overlappedWriteEvent) CloseHandle(posDev->overlappedWriteEvent);
  93. #endif
  94. GlobalFree(posDev);
  95. }
  96. VOID EnqueuePOSDevice(posDevice *posDev)
  97. {
  98. ASSERT(IsListEmpty(&posDev->listEntry));
  99. InsertTailList(&allPOSDevicesList, &posDev->listEntry);
  100. numDeviceInstances++;
  101. }
  102. VOID DequeuePOSDevice(posDevice *posDev)
  103. {
  104. ASSERT(!IsListEmpty(&allPOSDevicesList));
  105. ASSERT(!IsListEmpty(&posDev->listEntry));
  106. RemoveEntryList(&posDev->listEntry);
  107. InitializeListHead(&posDev->listEntry);
  108. numDeviceInstances--;
  109. }
  110. posDevice *GetDeviceByHDlg(HWND hDlg)
  111. {
  112. posDevice *foundPosDev = NULL;
  113. LIST_ENTRY *listEntry;
  114. listEntry = &allPOSDevicesList;
  115. while ((listEntry = listEntry->Flink) != &allPOSDevicesList){
  116. posDevice *thisPosDev;
  117. thisPosDev = CONTAINING_RECORD(listEntry, posDevice, listEntry);
  118. if (thisPosDev->hDlg == hDlg){
  119. foundPosDev = thisPosDev;
  120. break;
  121. }
  122. }
  123. return foundPosDev;
  124. }
  125. VOID OpenAllHIDPOSDevices()
  126. {
  127. HDEVINFO hDevInfo;
  128. GUID hidGuid = {0};
  129. WCHAR devicePath[MAX_PATH];
  130. /*
  131. * Call hid.dll to get the GUID for Human Input Devices.
  132. */
  133. HidD_GetHidGuid(&hidGuid);
  134. hDevInfo = SetupDiGetClassDevs( &hidGuid,
  135. NULL,
  136. NULL,
  137. DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  138. if (hDevInfo == INVALID_HANDLE_VALUE){
  139. DWORD err = GetLastError();
  140. DBGERR(L"SetupDiGetClassDevs failed", err);
  141. }
  142. else {
  143. int i;
  144. for (i = 0; TRUE; i++){
  145. SP_DEVICE_INTERFACE_DATA devInfoData = {0};
  146. BOOL ok;
  147. devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  148. ok = SetupDiEnumDeviceInterfaces( hDevInfo,
  149. 0,
  150. &hidGuid,
  151. i,
  152. &devInfoData);
  153. if (ok){
  154. DWORD hwDetailLen = 0;
  155. /*
  156. * Call SetupDiGetDeviceInterfaceDetail with
  157. * a NULL PSP_DEVICE_INTERFACE_DETAIL_DATA pointer
  158. * just to get the length of the hardware details.
  159. */
  160. ASSERT(devInfoData.cbSize == sizeof(SP_DEVICE_INTERFACE_DATA));
  161. ok = SetupDiGetDeviceInterfaceDetail(
  162. hDevInfo,
  163. &devInfoData,
  164. NULL,
  165. 0,
  166. &hwDetailLen,
  167. NULL);
  168. if (ok || (GetLastError() == ERROR_INSUFFICIENT_BUFFER)){
  169. PSP_DEVICE_INTERFACE_DETAIL_DATA devDetails;
  170. /*
  171. * Now make the real call to SetupDiGetDeviceInterfaceDetail.
  172. */
  173. ASSERT(hwDetailLen > sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
  174. devDetails = GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, hwDetailLen);
  175. if (devDetails){
  176. devDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  177. ok = SetupDiGetDeviceInterfaceDetail(
  178. hDevInfo,
  179. &devInfoData,
  180. devDetails,
  181. hwDetailLen,
  182. &hwDetailLen,
  183. NULL);
  184. if (ok){
  185. /*
  186. * BUGBUG - FINISH
  187. * Right now, we only handle cash drawers.
  188. * And we only work with the APG cash drawers
  189. * (with vendor-specific usages) for now.
  190. */
  191. WCHAR apgKbPathPrefix[] = L"\\\\?\\hid#vid_0f25&pid_0500";
  192. /*
  193. * If this is an APG keyboard, then the device path
  194. * (very long) will begin with apgKbPathPrefix.
  195. */
  196. if (RtlEqualMemory( devDetails->DevicePath,
  197. apgKbPathPrefix,
  198. sizeof(apgKbPathPrefix)-sizeof(WCHAR))){
  199. HANDLE hDev;
  200. // MessageBox(NULL, devDetails->DevicePath, L"DEBUG message - found APG kb", MB_OK);
  201. hDev = CreateFile(
  202. devDetails->DevicePath,
  203. GENERIC_READ | GENERIC_WRITE,
  204. FILE_SHARE_READ | FILE_SHARE_WRITE,
  205. NULL,
  206. OPEN_EXISTING,
  207. 0,
  208. NULL);
  209. if (hDev == INVALID_HANDLE_VALUE){
  210. DWORD err = GetLastError();
  211. DBGERR(L"CreateFile failed", err);
  212. }
  213. else {
  214. PHIDP_PREPARSED_DATA hidPreparsedData;
  215. // MessageBox(NULL, devDetails->DevicePath, L"DEBUG message - CreateFile succeeded", MB_OK);
  216. ok = HidD_GetPreparsedData(hDev, &hidPreparsedData);
  217. if (ok){
  218. HIDD_ATTRIBUTES hidAttrib;
  219. ok = HidD_GetAttributes(hDev, &hidAttrib);
  220. if (ok){
  221. HIDP_CAPS hidCapabilities;
  222. ok = HidP_GetCaps(hidPreparsedData, &hidCapabilities);
  223. if (ok){
  224. posDevice *posDev;
  225. posDev = NewPOSDevice( IDD_POS_CASHDRAWER_DLG,
  226. hDev,
  227. devDetails->DevicePath,
  228. hidPreparsedData,
  229. &hidAttrib,
  230. &hidCapabilities);
  231. if (posDev){
  232. EnqueuePOSDevice(posDev);
  233. }
  234. else {
  235. ASSERT(posDev);
  236. }
  237. }
  238. else {
  239. DBGERR(L"HidP_GetCaps failed", 0);
  240. }
  241. }
  242. else {
  243. DWORD err = GetLastError();
  244. DBGERR(L"HidD_GetAttributes failed", err);
  245. }
  246. }
  247. else {
  248. DWORD err = GetLastError();
  249. DBGERR(L"HidD_GetPreparsedData failed", err);
  250. }
  251. }
  252. }
  253. }
  254. else {
  255. DWORD err = GetLastError();
  256. DBGERR(L"SetupDiGetDeviceInterfaceDetail(2) failed", err);
  257. }
  258. GlobalFree(devDetails);
  259. }
  260. }
  261. else {
  262. DWORD err = GetLastError();
  263. DBGERR(L"SetupDiGetDeviceInterfaceDetail(1) failed", err);
  264. }
  265. }
  266. else {
  267. DWORD err = GetLastError();
  268. if (err != ERROR_NO_MORE_ITEMS){
  269. DBGERR(L"SetupDiEnumDeviceInterfaces failed", err);
  270. }
  271. break;
  272. }
  273. }
  274. SetupDiDestroyDeviceInfoList(hDevInfo);
  275. }
  276. }
  277. /*
  278. * LaunchDeviceInstanceThread
  279. *
  280. * Launch a thread for a device instance to read
  281. * asynchronous events from the device.
  282. */
  283. VOID LaunchDeviceInstanceThread(posDevice *posDev)
  284. {
  285. DWORD threadId;
  286. posDev->hThread = CreateThread(NULL, 0, DeviceInstanceThread, posDev, 0, &threadId);
  287. if (posDev->hThread){
  288. }
  289. else {
  290. DWORD err = GetLastError();
  291. DBGERR(L"CreateThread failed", err);
  292. }
  293. }
  294. #if USE_OVERLAPPED_IO
  295. VOID CALLBACK OverlappedReadCompletionRoutine( DWORD dwErrorCode,
  296. DWORD dwNumberOfBytesTransfered,
  297. LPOVERLAPPED lpOverlapped)
  298. {
  299. posDevice *posDev;
  300. /*
  301. * We stashed our context in the hEvent field of the
  302. * overlapped structure (this is allowed).
  303. */
  304. ASSERT(lpOverlapped);
  305. posDev = lpOverlapped->hEvent;
  306. ASSERT(posDev->sig == POSCPL_SIG);
  307. posDev->overlappedReadStatus = dwErrorCode;
  308. posDev->overlappedReadLen = dwNumberOfBytesTransfered;
  309. SetEvent(posDev->overlappedReadEvent);
  310. }
  311. #endif
  312. DWORD __stdcall DeviceInstanceThread(void *context)
  313. {
  314. posDevice *posDev = (posDevice *)context;
  315. HANDLE hDevNew;
  316. ASSERT(posDev->sig == POSCPL_SIG);
  317. // BUGBUG - for some reason, reads and writes on the same handle
  318. // interfere with one another
  319. hDevNew = CreateFile(
  320. posDev->pathName,
  321. GENERIC_READ,
  322. FILE_SHARE_READ | FILE_SHARE_WRITE,
  323. NULL,
  324. OPEN_EXISTING,
  325. 0,
  326. NULL);
  327. if (hDevNew == INVALID_HANDLE_VALUE){
  328. DWORD err = GetLastError();
  329. DBGERR(L"CreateFile failed", err);
  330. }
  331. else {
  332. /*
  333. * Loop forever until main thread kills this thread.
  334. */
  335. while (TRUE){
  336. WCHAR drawerStateString[100];
  337. DWORD bytesRead = 0;
  338. BOOL ok;
  339. /*
  340. * Load the default string for the drawer state
  341. */
  342. LoadString(g_hInst, IDS_DRAWERSTATE_UNKNOWN, drawerStateString, 100);
  343. ASSERT(posDev->hidCapabilities.InputReportByteLength > 0);
  344. ASSERT(posDev->readBuffer);
  345. #if USE_OVERLAPPED_IO
  346. /*
  347. * It's ok to stash our context in the hEvent field
  348. * of the overlapped structure.
  349. */
  350. posDev->overlappedReadInfo.hEvent = (HANDLE)posDev;
  351. posDev->overlappedReadInfo.Offset = 0;
  352. posDev->overlappedReadInfo.OffsetHigh = 0;
  353. posDev->overlappedReadLen = 0;
  354. ResetEvent(posDev->overlappedReadEvent);
  355. ok = ReadFileEx(hDevNew,
  356. posDev->readBuffer,
  357. posDev->hidCapabilities.InputReportByteLength,
  358. &posDev->overlappedReadInfo,
  359. OverlappedReadCompletionRoutine);
  360. if (ok){
  361. WaitForSingleObject(posDev->overlappedReadEvent, INFINITE);
  362. ok = (posDev->overlappedReadStatus == NO_ERROR);
  363. bytesRead = posDev->overlappedWriteLen;
  364. }
  365. else {
  366. bytesRead = 0;
  367. }
  368. #else
  369. ok = ReadFile( hDevNew,
  370. posDev->readBuffer,
  371. posDev->hidCapabilities.InputReportByteLength,
  372. &bytesRead,
  373. NULL);
  374. #endif
  375. if (ok){
  376. NTSTATUS ntStat;
  377. ULONG usageVal;
  378. ASSERT(bytesRead <= posDev->hidCapabilities.InputReportByteLength);
  379. ntStat = HidP_GetUsageValue(HidP_Input,
  380. USAGE_PAGE_CASH_DEVICE,
  381. 0, // all collections
  382. USAGE_CASH_DRAWER_STATUS,
  383. &usageVal,
  384. posDev->hidPreparsedData,
  385. posDev->readBuffer,
  386. posDev->hidCapabilities.InputReportByteLength);
  387. if (ntStat == HIDP_STATUS_SUCCESS){
  388. HWND hOpenButton;
  389. /*
  390. * Get display string for new drawer state.
  391. */
  392. switch (usageVal){
  393. case DRAWER_STATE_OPEN:
  394. LoadString(g_hInst, IDS_DRAWERSTATE_OPEN, drawerStateString, 100);
  395. break;
  396. case DRAWER_STATE_CLOSED_READY:
  397. LoadString(g_hInst, IDS_DRAWERSTATE_READY, drawerStateString, 100);
  398. break;
  399. case DRAWER_STATE_CLOSED_CHARGING:
  400. LoadString(g_hInst, IDS_DRAWERSTATE_CHARGING, drawerStateString, 100);
  401. break;
  402. case DRAWER_STATE_LOCKED:
  403. LoadString(g_hInst, IDS_DRAWERSTATE_LOCKED, drawerStateString, 100);
  404. break;
  405. default:
  406. DBGERR(L"illegal usage", usageVal);
  407. break;
  408. }
  409. /*
  410. * Set 'Open' button based on the drawer state.
  411. */
  412. hOpenButton = GetDlgItem(posDev->hDlg, IDC_CASHDRAWER_OPEN);
  413. if (hOpenButton){
  414. LONG btnState = GetWindowLong(hOpenButton, GWL_STYLE);
  415. switch (usageVal){
  416. case DRAWER_STATE_OPEN:
  417. btnState |= WS_DISABLED;
  418. break;
  419. default:
  420. btnState &= ~WS_DISABLED;
  421. break;
  422. }
  423. SetWindowLong(hOpenButton, GWL_STYLE, btnState);
  424. /*
  425. * To make SetWindowLong take effect, you
  426. * sometimes have to call SetWindowPos.
  427. */
  428. SetWindowPos(hOpenButton, 0,
  429. 0, 0, 0, 0,
  430. SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_HIDEWINDOW);
  431. SetWindowPos(hOpenButton, 0,
  432. 0, 0, 0, 0,
  433. SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_SHOWWINDOW);
  434. }
  435. else {
  436. DBGERR(L"GetDlgItem failed", 0);
  437. }
  438. }
  439. else {
  440. DBGERR(L"HidP_GetUsageValue failed", ntStat);
  441. DBGERR(DBGHIDSTATUSSTR(ntStat), 0);
  442. }
  443. }
  444. else {
  445. DWORD err = GetLastError();
  446. DBGERR(L"ReadFile failed", err);
  447. }
  448. ASSERT(posDev->hDlg);
  449. ok = SetDlgItemText(posDev->hDlg, IDC_CASHDRAWER_STATETEXT, drawerStateString);
  450. if (ok){
  451. }
  452. else {
  453. DWORD err = GetLastError();
  454. DBGERR(L"SetDlgItemText failed", err);
  455. }
  456. }
  457. CloseHandle(hDevNew);
  458. }
  459. return NO_ERROR;
  460. }