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.

548 lines
21 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. // APG Cash Drawer wrote to say their VID is actually 1989
  192. // WCHAR apgKbPathPrefix[] = L"\\\\?\\hid#vid_0f25&pid_0500";
  193. // Correct APG Cash Drawer VID (1989) to hexcode (07C5)
  194. // WCHAR apgKbPathPrefix[] = L"\\\\?\\hid#vid_1989&pid_0500";
  195. WCHAR apgKbPathPrefix[] = L"\\\\?\\hid#vid_07c5&pid_0500";
  196. /*
  197. * If this is an APG keyboard, then the device path
  198. * (very long) will begin with apgKbPathPrefix.
  199. */
  200. if (RtlEqualMemory( devDetails->DevicePath,
  201. apgKbPathPrefix,
  202. sizeof(apgKbPathPrefix)-sizeof(WCHAR))){
  203. HANDLE hDev;
  204. // MessageBox(NULL, devDetails->DevicePath, L"DEBUG message - found APG kb", MB_OK);
  205. hDev = CreateFile(
  206. devDetails->DevicePath,
  207. GENERIC_READ | GENERIC_WRITE,
  208. FILE_SHARE_READ | FILE_SHARE_WRITE,
  209. NULL,
  210. OPEN_EXISTING,
  211. 0,
  212. NULL);
  213. if (hDev == INVALID_HANDLE_VALUE){
  214. DWORD err = GetLastError();
  215. DBGERR(L"CreateFile failed", err);
  216. }
  217. else {
  218. PHIDP_PREPARSED_DATA hidPreparsedData;
  219. // MessageBox(NULL, devDetails->DevicePath, L"DEBUG message - CreateFile succeeded", MB_OK);
  220. ok = HidD_GetPreparsedData(hDev, &hidPreparsedData);
  221. if (ok){
  222. HIDD_ATTRIBUTES hidAttrib;
  223. ok = HidD_GetAttributes(hDev, &hidAttrib);
  224. if (ok){
  225. HIDP_CAPS hidCapabilities;
  226. ok = HidP_GetCaps(hidPreparsedData, &hidCapabilities);
  227. if (ok){
  228. posDevice *posDev;
  229. posDev = NewPOSDevice( IDD_POS_CASHDRAWER_DLG,
  230. hDev,
  231. devDetails->DevicePath,
  232. hidPreparsedData,
  233. &hidAttrib,
  234. &hidCapabilities);
  235. if (posDev){
  236. EnqueuePOSDevice(posDev);
  237. }
  238. else {
  239. ASSERT(posDev);
  240. }
  241. }
  242. else {
  243. DBGERR(L"HidP_GetCaps failed", 0);
  244. }
  245. }
  246. else {
  247. DWORD err = GetLastError();
  248. DBGERR(L"HidD_GetAttributes failed", err);
  249. }
  250. }
  251. else {
  252. DWORD err = GetLastError();
  253. DBGERR(L"HidD_GetPreparsedData failed", err);
  254. }
  255. }
  256. }
  257. }
  258. else {
  259. DWORD err = GetLastError();
  260. DBGERR(L"SetupDiGetDeviceInterfaceDetail(2) failed", err);
  261. }
  262. GlobalFree(devDetails);
  263. }
  264. }
  265. else {
  266. DWORD err = GetLastError();
  267. DBGERR(L"SetupDiGetDeviceInterfaceDetail(1) failed", err);
  268. }
  269. }
  270. else {
  271. DWORD err = GetLastError();
  272. if (err != ERROR_NO_MORE_ITEMS){
  273. DBGERR(L"SetupDiEnumDeviceInterfaces failed", err);
  274. }
  275. break;
  276. }
  277. }
  278. SetupDiDestroyDeviceInfoList(hDevInfo);
  279. }
  280. }
  281. /*
  282. * LaunchDeviceInstanceThread
  283. *
  284. * Launch a thread for a device instance to read
  285. * asynchronous events from the device.
  286. */
  287. VOID LaunchDeviceInstanceThread(posDevice *posDev)
  288. {
  289. DWORD threadId;
  290. posDev->hThread = CreateThread(NULL, 0, DeviceInstanceThread, posDev, 0, &threadId);
  291. if (posDev->hThread){
  292. }
  293. else {
  294. DWORD err = GetLastError();
  295. DBGERR(L"CreateThread failed", err);
  296. }
  297. }
  298. #if USE_OVERLAPPED_IO
  299. VOID CALLBACK OverlappedReadCompletionRoutine( DWORD dwErrorCode,
  300. DWORD dwNumberOfBytesTransfered,
  301. LPOVERLAPPED lpOverlapped)
  302. {
  303. posDevice *posDev;
  304. /*
  305. * We stashed our context in the hEvent field of the
  306. * overlapped structure (this is allowed).
  307. */
  308. ASSERT(lpOverlapped);
  309. posDev = lpOverlapped->hEvent;
  310. ASSERT(posDev->sig == POSCPL_SIG);
  311. posDev->overlappedReadStatus = dwErrorCode;
  312. posDev->overlappedReadLen = dwNumberOfBytesTransfered;
  313. SetEvent(posDev->overlappedReadEvent);
  314. }
  315. #endif
  316. DWORD __stdcall DeviceInstanceThread(void *context)
  317. {
  318. posDevice *posDev = (posDevice *)context;
  319. HANDLE hDevNew;
  320. ASSERT(posDev->sig == POSCPL_SIG);
  321. // BUGBUG - for some reason, reads and writes on the same handle
  322. // interfere with one another
  323. hDevNew = CreateFile(
  324. posDev->pathName,
  325. GENERIC_READ,
  326. FILE_SHARE_READ | FILE_SHARE_WRITE,
  327. NULL,
  328. OPEN_EXISTING,
  329. 0,
  330. NULL);
  331. if (hDevNew == INVALID_HANDLE_VALUE){
  332. DWORD err = GetLastError();
  333. DBGERR(L"CreateFile failed", err);
  334. }
  335. else {
  336. /*
  337. * Loop forever until main thread kills this thread.
  338. */
  339. while (TRUE){
  340. WCHAR drawerStateString[100];
  341. DWORD bytesRead = 0;
  342. BOOL ok;
  343. /*
  344. * Load the default string for the drawer state
  345. */
  346. LoadString(g_hInst, IDS_DRAWERSTATE_UNKNOWN, drawerStateString, 100);
  347. ASSERT(posDev->hidCapabilities.InputReportByteLength > 0);
  348. ASSERT(posDev->readBuffer);
  349. #if USE_OVERLAPPED_IO
  350. /*
  351. * It's ok to stash our context in the hEvent field
  352. * of the overlapped structure.
  353. */
  354. posDev->overlappedReadInfo.hEvent = (HANDLE)posDev;
  355. posDev->overlappedReadInfo.Offset = 0;
  356. posDev->overlappedReadInfo.OffsetHigh = 0;
  357. posDev->overlappedReadLen = 0;
  358. ResetEvent(posDev->overlappedReadEvent);
  359. ok = ReadFileEx(hDevNew,
  360. posDev->readBuffer,
  361. posDev->hidCapabilities.InputReportByteLength,
  362. &posDev->overlappedReadInfo,
  363. OverlappedReadCompletionRoutine);
  364. if (ok){
  365. WaitForSingleObject(posDev->overlappedReadEvent, INFINITE);
  366. ok = (posDev->overlappedReadStatus == NO_ERROR);
  367. bytesRead = posDev->overlappedWriteLen;
  368. }
  369. else {
  370. bytesRead = 0;
  371. }
  372. #else
  373. ok = ReadFile( hDevNew,
  374. posDev->readBuffer,
  375. posDev->hidCapabilities.InputReportByteLength,
  376. &bytesRead,
  377. NULL);
  378. #endif
  379. if (ok){
  380. NTSTATUS ntStat;
  381. ULONG usageVal;
  382. ASSERT(bytesRead <= posDev->hidCapabilities.InputReportByteLength);
  383. ntStat = HidP_GetUsageValue(HidP_Input,
  384. USAGE_PAGE_CASH_DEVICE,
  385. 0, // all collections
  386. USAGE_CASH_DRAWER_STATUS,
  387. &usageVal,
  388. posDev->hidPreparsedData,
  389. posDev->readBuffer,
  390. posDev->hidCapabilities.InputReportByteLength);
  391. if (ntStat == HIDP_STATUS_SUCCESS){
  392. HWND hOpenButton;
  393. /*
  394. * Get display string for new drawer state.
  395. */
  396. switch (usageVal){
  397. case DRAWER_STATE_OPEN:
  398. LoadString(g_hInst, IDS_DRAWERSTATE_OPEN, drawerStateString, 100);
  399. break;
  400. case DRAWER_STATE_CLOSED_READY:
  401. LoadString(g_hInst, IDS_DRAWERSTATE_READY, drawerStateString, 100);
  402. break;
  403. case DRAWER_STATE_CLOSED_CHARGING:
  404. LoadString(g_hInst, IDS_DRAWERSTATE_CHARGING, drawerStateString, 100);
  405. break;
  406. case DRAWER_STATE_LOCKED:
  407. LoadString(g_hInst, IDS_DRAWERSTATE_LOCKED, drawerStateString, 100);
  408. break;
  409. default:
  410. DBGERR(L"illegal usage", usageVal);
  411. break;
  412. }
  413. /*
  414. * Set 'Open' button based on the drawer state.
  415. */
  416. hOpenButton = GetDlgItem(posDev->hDlg, IDC_CASHDRAWER_OPEN);
  417. if (hOpenButton){
  418. LONG btnState = GetWindowLong(hOpenButton, GWL_STYLE);
  419. switch (usageVal){
  420. case DRAWER_STATE_OPEN:
  421. btnState |= WS_DISABLED;
  422. break;
  423. default:
  424. btnState &= ~WS_DISABLED;
  425. break;
  426. }
  427. SetWindowLong(hOpenButton, GWL_STYLE, btnState);
  428. /*
  429. * To make SetWindowLong take effect, you
  430. * sometimes have to call SetWindowPos.
  431. */
  432. SetWindowPos(hOpenButton, 0,
  433. 0, 0, 0, 0,
  434. SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_HIDEWINDOW);
  435. SetWindowPos(hOpenButton, 0,
  436. 0, 0, 0, 0,
  437. SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_SHOWWINDOW);
  438. }
  439. else {
  440. DBGERR(L"GetDlgItem failed", 0);
  441. }
  442. }
  443. else {
  444. DBGERR(L"HidP_GetUsageValue failed", ntStat);
  445. DBGERR(DBGHIDSTATUSSTR(ntStat), 0);
  446. }
  447. }
  448. else {
  449. DWORD err = GetLastError();
  450. DBGERR(L"ReadFile failed", err);
  451. }
  452. ASSERT(posDev->hDlg);
  453. ok = SetDlgItemText(posDev->hDlg, IDC_CASHDRAWER_STATETEXT, drawerStateString);
  454. if (ok){
  455. }
  456. else {
  457. DWORD err = GetLastError();
  458. DBGERR(L"SetDlgItemText failed", err);
  459. }
  460. }
  461. CloseHandle(hDevNew);
  462. }
  463. return NO_ERROR;
  464. }