/***************************************************************************** * * DIHel.c * * Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Hardware emulation layer for DirectInput. * * Contents: * * Hel_AcquireInstance * Hel_UnacquireInstance * Hel_SetBufferSize * Hel_DestroyInstance * * Hel_SetDataFormat * Hel_SetNotifyHandle * * Hel_Mouse_CreateInstance * Hel_Kbd_CreateInstance * Hel_Kbd_InitKeys * Hel_Joy_CreateInstance * Hel_Joy_Ping * Hel_Joy_GetInitParms * * IoctlHw * *****************************************************************************/ #include "dinputpr.h" /***************************************************************************** * * The sqiffle for this file. * *****************************************************************************/ #define sqfl sqflHel /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | IoctlHw | * * Send the IOCtl to the hardware device. * * @parm DWORD | ioctl | * * I/O control code. * * @parm IN LPVOID | pvIn | * * Optional input parameter. * * @parm DWORD | cbIn | * * Size of input buffer in bytes. * * @parm IN LPVOID | pvOut | * * Optional output parameter. * * @parm DWORD | cbOut | * * Size of output buffer in bytes. * * @returns * * if the ioctl succeeded and returned the correct * number of bytes, else something based on the Win32 error code. * *****************************************************************************/ #ifndef WINNT HRESULT EXTERNAL IoctlHw(DWORD ioctl, LPVOID pvIn, DWORD cbIn, LPVOID pvOut, DWORD cbOut) { HRESULT hres; DWORD cbRc; if (g_hVxD != INVALID_HANDLE_VALUE) { if (DeviceIoControl(g_hVxD, ioctl, pvIn, cbIn, pvOut, cbOut, &cbRc, 0)) { if (cbRc == cbOut) { hres = S_OK; } else { SquirtSqflPtszV(sqfl, TEXT("Ioctl(%08x) returned wrong cbOut"), ioctl); hres = DIERR_BADDRIVERVER; } } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("Ioctl(%08x) failed, error %d"), ioctl, GetLastError()); hres = hresLe(GetLastError()); } } else { hres = DIERR_BADDRIVERVER; } return hres; } #endif /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_IoctlChoose | * * Send the IOCtl to the hardware device if it is native, * or perform the operation through emulation if it is emulated. * * @parm PVXDINSTANCE | pvi | * * The device in question. * * @parm PFNHANDLER | pfn | * * The emulation function to call to carry out the operation. * * @parm DWORD | ioctl | * * I/O control code. * * @parm IN LPVOID | pvIn | * * Optional input parameter. * * @parm DWORD | cbIn | * * Size of input buffer in bytes. * *****************************************************************************/ typedef HRESULT (EXTERNAL *PFNHANDLER)(PV pv); HRESULT INTERNAL Hel_IoctlChoose(PVXDINSTANCE pvi, PFNHANDLER pfn, DWORD ioctl, LPVOID pvIn, DWORD cbIn) { HRESULT hres; if (!(pvi->fl & VIFL_EMULATED)) { hres = IoctlHw(ioctl, pvIn, cbIn, 0, 0); } else { hres = pfn(pvIn); } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_AcquireInstance | * * Attempt to acquire the device instance, using either the * device driver or emulation, whichever is appropriate. * * @parm PVXDINSTANCE | pvi | * * The instance to acquire. * *****************************************************************************/ HRESULT EXTERNAL Hel_AcquireInstance(PVXDINSTANCE pvi) { return Hel_IoctlChoose(pvi, CEm_AcquireInstance, IOCTL_ACQUIREINSTANCE, &pvi, cbX(pvi)); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_UnacquireInstance | * * Attempt to unacquire the device instance. * * @parm PVXDINSTANCE | pvi | * * The instance to unacquire. * *****************************************************************************/ HRESULT EXTERNAL Hel_UnacquireInstance(PVXDINSTANCE pvi) { return Hel_IoctlChoose(pvi, CEm_UnacquireInstance, IOCTL_UNACQUIREINSTANCE, &pvi, cbX(pvi)); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_SetBufferSize | * * Set the buffer size. * * @parm PVXDDWORDDATA | pvdd | * * Information about the buffer size. * *****************************************************************************/ HRESULT EXTERNAL Hel_SetBufferSize(PVXDDWORDDATA pvdd) { HRESULT hres; EnterProc(Hel_SetBufferSize, (_ "pxx", pvdd->pvi, pvdd->dw, pvdd->pvi->fl)); hres = Hel_IoctlChoose(pvdd->pvi, CEm_SetBufferSize, IOCTL_SETBUFFERSIZE, pvdd, cbX(*pvdd)); ExitOleProc(); return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_DestroyInstance | * * Destroy the device instance in the appropriate way. * * @parm PVXDINSTANCE | pvi | * * The instance. * *****************************************************************************/ HRESULT EXTERNAL Hel_DestroyInstance(PVXDINSTANCE pvi) { return Hel_IoctlChoose(pvi, CEm_DestroyInstance, IOCTL_DESTROYINSTANCE, &pvi, cbX(pvi)); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_SetDataFormat | * * Set the data format. * * @parm PVXDDATAFORMAT | pvdf | * * Information about the data format. * *****************************************************************************/ HRESULT EXTERNAL Hel_SetDataFormat(PVXDDATAFORMAT pvdf) { return Hel_IoctlChoose(pvdf->pvi, CEm_SetDataFormat, IOCTL_SETDATAFORMAT, pvdf, cbX(*pvdf)); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_SetNotifyHandle | * * Set the event handle for notification. * * @parm PVXDDWORDDATA | pvdd | * * 9x: dw = ring 0 handle. Dinput calls _OpenVxDHandle to get ring 0 handle. * NT: dw = ring 3 handle. DINPUT.SYS translates the handle to pointer. * *****************************************************************************/ HRESULT EXTERNAL Hel_SetNotifyHandle(PVXDDWORDDATA pvdd) { HRESULT hres; if (!(pvdd->pvi->fl & VIFL_EMULATED)) { #ifndef WINNT AssertF(_OpenVxDHandle); if (pvdd->dw) { pvdd->dw = _OpenVxDHandle((HANDLE)pvdd->dw); } #endif hres = IoctlHw(IOCTL_SETNOTIFYHANDLE, pvdd, cbX(*pvdd), 0, 0); } else { hres = S_OK; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @struct CREATEDEVICEINFO | * * Describes how to create the device either via the driver or * via emulation. * * @parm DWORD | dwIoctl | * * IOCtl code to try. * * @parm DWORD | flEmulation | * * Flag in registry that forces emulation. * * @parm EMULATIONCREATEPROC | pfnCreate | * * Function that creates emulation object. * *****************************************************************************/ #pragma BEGIN_CONST_DATA typedef HRESULT (EXTERNAL *EMULATIONCREATEPROC) (PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut); typedef struct CREATEDEVICEINFO { DWORD dwIoctl; DWORD flEmulation; EMULATIONCREATEPROC pfnCreate; } CREATEDEVICEINFO, *PCREATEDEVICEINFO; CREATEDEVICEINFO c_cdiMouse = { IOCTL_MOUSE_CREATEINSTANCE, DIEMFL_MOUSE, CEm_Mouse_CreateInstance, }; CREATEDEVICEINFO c_cdiKbd = { IOCTL_KBD_CREATEINSTANCE, DIEMFL_KBD | DIEMFL_KBD2, CEm_Kbd_CreateInstance, }; CREATEDEVICEINFO c_cdiJoy = { IOCTL_JOY_CREATEINSTANCE, DIEMFL_JOYSTICK, CEm_Joy_CreateInstance, }; #pragma END_CONST_DATA /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_CreateInstance | * * Attempt to create the device instance through the driver * with the specified IOCtl. * * If that is not possible, then use the emulation callback. * * @parm PCREATEDEVICEINFO | pcdi | * * Describes how to create the device. * * @parm PVXDDEVICEFORMAT | pdevf | * * Describes the device being created. * * @parm PVXDINSTANCE * | ppviOut | * * Receives created instance. * *****************************************************************************/ HRESULT EXTERNAL Hel_CreateInstance(PCREATEDEVICEINFO pcdi, PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut) { HRESULT hres; pdevf->dwEmulation |= g_flEmulation; pdevf->dwEmulation &= pcdi->flEmulation; if (pdevf->dwEmulation || (FAILED(hres = IoctlHw(pcdi->dwIoctl, pdevf, cbX(*pdevf), ppviOut, cbX(*ppviOut))))) { hres = pcdi->pfnCreate(pdevf, ppviOut); } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Mouse_CreateInstance | * * Attempt to create the device instance through the driver. * If that is not possible, then use the emulation layer. * * @parm PVXDDEVICEFORMAT | pdevf | * * Describes the device being created. * * @parm PVXDINSTANCE * | ppviOut | * * Receives created instance. * *****************************************************************************/ HRESULT EXTERNAL Hel_Mouse_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut) { return Hel_CreateInstance(&c_cdiMouse, pdevf, ppviOut); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Kbd_CreateInstance | * * Attempt to create the device instance through the driver. * If that is not possible, then use the emulation layer. * * @parm PVXDDEVICEFORMAT | pdevf | * * Describes the device being created. * * @parm PVXDINSTANCE * | ppviOut | * * Receives created instance. * *****************************************************************************/ HRESULT EXTERNAL Hel_Kbd_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut) { return Hel_CreateInstance(&c_cdiKbd, pdevf, ppviOut); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Kbd_InitKeys | * * Tell the device driver (or emulation) about the key state. * * @parm PVXDDWORDDATA | pvdd | * * The instance and the key state. * *****************************************************************************/ HRESULT EXTERNAL Hel_Kbd_InitKeys(PVXDDWORDDATA pvdd) { return Hel_IoctlChoose(pvdd->pvi, CEm_Kbd_InitKeys, IOCTL_KBD_INITKEYS, pvdd, cbX(*pvdd)); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Joy_CreateInstance | * * Attempt to create the device instance through the driver. * If that is not possible, then use the emulation layer. * * @parm PVXDDEVICEFORMAT | pdevf | * * Describes the device being created. * * @parm PVXDINSTANCE * | ppviOut | * * Receives created instance. * *****************************************************************************/ HRESULT EXTERNAL Hel_Joy_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut) { return Hel_CreateInstance(&c_cdiJoy, pdevf, ppviOut); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Joy_Ping | * * Ask the device driver (or emulation) to get the joystick info. * * @parm PVXDDWORDDATA | pvdd | * * The instance and the key state. * *****************************************************************************/ HRESULT EXTERNAL Hel_Joy_Ping(PVXDINSTANCE pvi) { return Hel_IoctlChoose(pvi, CEm_Joy_Ping, IOCTL_JOY_PING8, &pvi, cbX(pvi)); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Joy_ConfigChanged | * * Tell vjoyd config has been changed. * *****************************************************************************/ HRESULT EXTERNAL Hel_Joy_ConfigChanged(DWORD dwFlags) { return IoctlHw(IOCTL_JOY_CONFIGCHANGED, &dwFlags, cbX(dwFlags), NULL, 0); } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Joy_GetInitParms | * * Ask the device driver (or emulation) for * VJOYD initialization parameters. * * In emulation, we assume the internal and external * IDs are equal (because they may as well be), * that no flags are set, and there are no versions. * * @parm DWORD | dwExternalID | * * The external joystick number. * * @parm PVXDINITPARMS | pvip | * * Receives assorted information. * *****************************************************************************/ HRESULT EXTERNAL Hel_Joy_GetInitParms(DWORD dwExternalID, PVXDINITPARMS pvip) { HRESULT hres; if ((g_flEmulation & DIEMFL_JOYSTICK) || FAILED(hres = IoctlHw(IOCTL_JOY_GETINITPARMS, &dwExternalID, cbX(dwExternalID), pvip, cbX(*pvip))) || FAILED(hres = pvip->hres)) { /* * Do it the emulation way. */ ZeroX(*pvip); pvip->dwId = dwExternalID; hres = S_OK; } return hres; } /***************************************************************************** * * @doc INTERNAL * * @func HRESULT | Hel_Joy_GetAxisCaps | * * Obtain a bitmask of the axes supported by the joystick. * If VJOYD won't tell us, then we figure it out from the * registry structure passed in. * * @parm DWORD | dwExternalID | * * The external joystick number. * * @parm PVXDAXISCAPS | pvac | * * Structure to receive the axis capabilities. * * @parm LPJOYREGHWCONFIG | phwc | * * The joystick settings as reported by the registry. * *****************************************************************************/ HRESULT EXTERNAL Hel_Joy_GetAxisCaps ( DWORD dwExternalID, PVXDAXISCAPS pvac, LPJOYREGHWCONFIG phwc ) { HRESULT hres; DWORD dwRegAxes; /* * Every joystick has an X and Y (no way to tell) * Mark as position axes or they won't count! */ dwRegAxes = JOYPF_X | JOYPF_Y | JOYPF_POSITION; if (phwc->hws.dwFlags & JOY_HWS_HASZ) { dwRegAxes |= JOYPF_Z; } if ( (phwc->hws.dwFlags & JOY_HWS_HASR) || (phwc->dwUsageSettings & JOY_US_HASRUDDER) ){ dwRegAxes |= JOYPF_R; } if (phwc->hws.dwFlags & JOY_HWS_HASU) { dwRegAxes |= JOYPF_U; } if (phwc->hws.dwFlags & JOY_HWS_HASV) { dwRegAxes |= JOYPF_V; } if ((g_flEmulation & DIEMFL_JOYSTICK) || FAILED(hres = IoctlHw(IOCTL_JOY_GETAXES, &dwExternalID, cbX(dwExternalID), pvac, cbX(*pvac)))) { /* * If that didn't work, then just use the registry. */ if (phwc->hws.dwFlags & JOY_HWS_HASPOV) { pvac->dwPos |= JOYPF_POV0; } /* * Old VJOYD clients do not support velocity or any of the * other stuff. */ pvac->dwVel = 0; pvac->dwAccel = 0; pvac->dwForce = 0; hres = S_OK; } else { /* * ManBug 28971: Logitech drivers (and probably others) respond to * probing on axes for which they do not report data so set the * position axes to whatever is reported in the registry. * Note this still allows multiple POVs to be picked up as only one * POV is supported by the registry flags. */ pvac->dwPos &= ~JOYPF_ALLAXES; pvac->dwPos |= dwRegAxes; } /* * CJoy_InitRing3 assumes that this never fails. */ AssertF(SUCCEEDED(hres)); return hres; }