|  | /* | 
|  | * joystick functions | 
|  | * | 
|  | * Copyright 1997 Andreas Mohr | 
|  | * Copyright 2000 Wolfgang Schwotzer | 
|  | * Copyright 2002 David Hagood | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | * | 
|  | * NOTES: | 
|  | * | 
|  | * - nearly all joystick functions can be regarded as obsolete, | 
|  | *   as Linux (2.1.x) now supports extended joysticks with a completely | 
|  | *   new joystick driver interface | 
|  | *   New driver's docu says: | 
|  | *     "For backward compatibility the old interface is still included, | 
|  | *     but will be dropped in the future." | 
|  | *   Thus we should implement the new interface and at most keep the old | 
|  | *   routines for backward compatibility. | 
|  | * - better support of enhanced joysticks (Linux 2.2 interface is available) | 
|  | * - support more joystick drivers (like the XInput extension) | 
|  | * - should load joystick DLL as any other driver (instead of hardcoding) | 
|  | *   the driver's name, and load it as any low lever driver. | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.h" | 
|  |  | 
|  | #ifdef HAVE_UNISTD_H | 
|  | # include <unistd.h> | 
|  | #endif | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <fcntl.h> | 
|  | #ifdef HAVE_SYS_IOCTL_H | 
|  | #include <sys/ioctl.h> | 
|  | #endif | 
|  | #ifdef HAVE_LINUX_IOCTL_H | 
|  | #include <linux/ioctl.h> | 
|  | #endif | 
|  | #ifdef HAVE_LINUX_JOYSTICK_H | 
|  | #include <linux/joystick.h> | 
|  | #ifdef SW_MAX | 
|  | #undef SW_MAX | 
|  | #endif | 
|  | #define JOYDEV_NEW "/dev/input/js%d" | 
|  | #define JOYDEV_OLD "/dev/js%d" | 
|  | #endif | 
|  | #ifdef HAVE_SYS_ERRNO_H | 
|  | #include <sys/errno.h> | 
|  | #endif | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  | #include "winuser.h" | 
|  | #include "winnls.h" | 
|  | #include "mmddk.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | #include "wine/unicode.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(joystick); | 
|  |  | 
|  | #ifdef HAVE_LINUX_JOYSTICK_H | 
|  |  | 
|  | #define MAXJOYSTICK	(JOYSTICKID2 + 1) | 
|  |  | 
|  | typedef struct tagWINE_JSTCK { | 
|  | int		joyIntf; | 
|  | int		in_use; | 
|  | /* Some extra info we need to make this acutaly work under the | 
|  | Linux 2.2 event api. | 
|  | First of all, we cannot keep closing and reopening the device file - | 
|  | that blows away the state of the stick device, and we lose events. So, we | 
|  | need to open the low-level device once, and close it when we are done. | 
|  |  | 
|  | Secondly, the event API only gives us what's changed. However, Windows apps | 
|  | want the whole state every time, so we have to cache the data. | 
|  | */ | 
|  |  | 
|  | int         dev; /* Linux level device file descriptor */ | 
|  | int         x; | 
|  | int         y; | 
|  | int         z; | 
|  | int         r; | 
|  | int         u; | 
|  | int         v; | 
|  | int         buttons; | 
|  | } WINE_JSTCK; | 
|  |  | 
|  | static	WINE_JSTCK	JSTCK_Data[MAXJOYSTICK]; | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				JSTCK_drvGet			[internal] | 
|  | */ | 
|  | static WINE_JSTCK *JSTCK_drvGet(DWORD_PTR dwDevID) | 
|  | { | 
|  | int	p; | 
|  |  | 
|  | if ((dwDevID - (DWORD_PTR)JSTCK_Data) % sizeof(JSTCK_Data[0]) != 0) | 
|  | return NULL; | 
|  | p = (dwDevID - (DWORD_PTR)JSTCK_Data) / sizeof(JSTCK_Data[0]); | 
|  | if (p < 0 || p >= MAXJOYSTICK || !((WINE_JSTCK*)dwDevID)->in_use) | 
|  | return NULL; | 
|  |  | 
|  | return (WINE_JSTCK*)dwDevID; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				JSTCK_drvOpen			[internal] | 
|  | */ | 
|  | static LRESULT JSTCK_drvOpen(LPSTR str, DWORD dwIntf) | 
|  | { | 
|  | if (dwIntf >= MAXJOYSTICK || JSTCK_Data[dwIntf].in_use) | 
|  | return 0; | 
|  |  | 
|  | JSTCK_Data[dwIntf].joyIntf = dwIntf; | 
|  | JSTCK_Data[dwIntf].in_use = 1; | 
|  | return (LRESULT)&JSTCK_Data[dwIntf]; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				JSTCK_drvClose			[internal] | 
|  | */ | 
|  | static LRESULT JSTCK_drvClose(DWORD_PTR dwDevID) | 
|  | { | 
|  | WINE_JSTCK*	jstck = JSTCK_drvGet(dwDevID); | 
|  |  | 
|  | if (jstck == NULL) | 
|  | return 0; | 
|  | jstck->in_use = 0; | 
|  | if (jstck->dev > 0) | 
|  | { | 
|  | close(jstck->dev); | 
|  | jstck->dev = 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | struct js_status | 
|  | { | 
|  | int buttons; | 
|  | int x; | 
|  | int y; | 
|  | }; | 
|  |  | 
|  | /************************************************************************** | 
|  | *                              JSTCK_OpenDevice           [internal] | 
|  | */ | 
|  | static	int	JSTCK_OpenDevice(WINE_JSTCK* jstick) | 
|  | { | 
|  | char	buf[20]; | 
|  | int		flags; | 
|  |  | 
|  | if (jstick->dev > 0) | 
|  | return jstick->dev; | 
|  |  | 
|  | sprintf(buf, JOYDEV_NEW, jstick->joyIntf); | 
|  | #ifdef HAVE_LINUX_22_JOYSTICK_API | 
|  | flags = O_RDONLY | O_NONBLOCK; | 
|  | #else | 
|  | flags = O_RDONLY; | 
|  | #endif | 
|  | if ((jstick->dev = open(buf, flags)) < 0) { | 
|  | sprintf(buf, JOYDEV_OLD, jstick->joyIntf); | 
|  | } | 
|  | return (jstick->dev = open(buf, flags)); | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				JoyGetDevCaps		[MMSYSTEM.102] | 
|  | */ | 
|  | static LRESULT JSTCK_GetDevCaps(DWORD_PTR dwDevID, LPJOYCAPSW lpCaps, DWORD dwSize) | 
|  | { | 
|  | WINE_JSTCK*	jstck; | 
|  | #ifdef HAVE_LINUX_22_JOYSTICK_API | 
|  | int		dev; | 
|  | char	nrOfAxes; | 
|  | char	nrOfButtons; | 
|  | char	identString[MAXPNAMELEN]; | 
|  | int		driverVersion; | 
|  | #else | 
|  | static const WCHAR ini[] = {'W','i','n','e',' ','J','o','y','s','t','i','c','k',' ','D','r','i','v','e','r',0}; | 
|  | #endif | 
|  |  | 
|  | if ((jstck = JSTCK_drvGet(dwDevID)) == NULL) | 
|  | return MMSYSERR_NODRIVER; | 
|  |  | 
|  | #ifdef HAVE_LINUX_22_JOYSTICK_API | 
|  | if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS; | 
|  | ioctl(dev, JSIOCGAXES, &nrOfAxes); | 
|  | ioctl(dev, JSIOCGBUTTONS, &nrOfButtons); | 
|  | ioctl(dev, JSIOCGVERSION, &driverVersion); | 
|  | ioctl(dev, JSIOCGNAME(sizeof(identString)), &identString); | 
|  | TRACE("Driver: 0x%06x, Name: %s, #Axes: %d, #Buttons: %d\n", | 
|  | driverVersion, identString, nrOfAxes, nrOfButtons); | 
|  | lpCaps->wMid = MM_MICROSOFT; | 
|  | lpCaps->wPid = MM_PC_JOYSTICK; | 
|  | MultiByteToWideChar(CP_ACP, 0, identString, -1, lpCaps->szPname, MAXPNAMELEN); | 
|  | lpCaps->szPname[MAXPNAMELEN-1] = '\0'; | 
|  | lpCaps->wXmin = 0; | 
|  | lpCaps->wXmax = 0xFFFF; | 
|  | lpCaps->wYmin = 0; | 
|  | lpCaps->wYmax = 0xFFFF; | 
|  | lpCaps->wZmin = 0; | 
|  | lpCaps->wZmax = (nrOfAxes >= 3) ? 0xFFFF : 0; | 
|  | #ifdef BODGE_THE_HAT | 
|  | /* HalfLife won't allow you to map an axis event to things like | 
|  | "next weapon" and "use". Linux reports the hat on my stick as | 
|  | axis U and V. So, IFF BODGE_THE_HAT is defined, lie through our | 
|  | teeth and say we have 32 buttons, and we will map the axises to | 
|  | the high buttons. Really, perhaps this should be a registry entry, | 
|  | or even a parameter to the Linux joystick driver (which would completely | 
|  | remove the need for this.) | 
|  | */ | 
|  | lpCaps->wNumButtons = 32; | 
|  | #else | 
|  | lpCaps->wNumButtons = nrOfButtons; | 
|  | #endif | 
|  | if (dwSize == sizeof(JOYCAPSW)) { | 
|  | /* since we suppose ntOfAxes <= 6 in the following code, do it explicitly */ | 
|  | if (nrOfAxes > 6) nrOfAxes = 6; | 
|  | /* complete 95 structure */ | 
|  | lpCaps->wRmin = 0; | 
|  | lpCaps->wRmax = nrOfAxes >= 4 ? 0xFFFF : 0; | 
|  | lpCaps->wUmin = 0; | 
|  | lpCaps->wUmax = nrOfAxes >= 5 ? 0xFFFF : 0; | 
|  | lpCaps->wVmin = 0; | 
|  | lpCaps->wVmax = nrOfAxes >= 6 ? 0xFFFF : 0; | 
|  | lpCaps->wMaxAxes = 6; /* same as MS Joystick Driver */ | 
|  | lpCaps->wNumAxes = nrOfAxes; /* nr of axes in use */ | 
|  | lpCaps->wMaxButtons = 32; /* same as MS Joystick Driver */ | 
|  | lpCaps->szRegKey[0] = 0; | 
|  | lpCaps->szOEMVxD[0] = 0; | 
|  | lpCaps->wCaps = 0; | 
|  | switch(nrOfAxes) { | 
|  | case 6: lpCaps->wCaps |= JOYCAPS_HASV; | 
|  | case 5: lpCaps->wCaps |= JOYCAPS_HASU; | 
|  | case 4: lpCaps->wCaps |= JOYCAPS_HASR; | 
|  | case 3: lpCaps->wCaps |= JOYCAPS_HASZ; | 
|  | /* FIXME: don't know how to detect for | 
|  | JOYCAPS_HASPOV, JOYCAPS_POV4DIR, JOYCAPS_POVCTS */ | 
|  | } | 
|  | } | 
|  | #else | 
|  | lpCaps->wMid = MM_MICROSOFT; | 
|  | lpCaps->wPid = MM_PC_JOYSTICK; | 
|  | strcpyW(lpCaps->szPname, ini); /* joystick product name */ | 
|  | lpCaps->wXmin = 0; | 
|  | lpCaps->wXmax = 0xFFFF; | 
|  | lpCaps->wYmin = 0; | 
|  | lpCaps->wYmax = 0xFFFF; | 
|  | lpCaps->wZmin = 0; | 
|  | lpCaps->wZmax = 0; | 
|  | lpCaps->wNumButtons = 2; | 
|  | if (dwSize == sizeof(JOYCAPSW)) { | 
|  | /* complete 95 structure */ | 
|  | lpCaps->wRmin = 0; | 
|  | lpCaps->wRmax = 0; | 
|  | lpCaps->wUmin = 0; | 
|  | lpCaps->wUmax = 0; | 
|  | lpCaps->wVmin = 0; | 
|  | lpCaps->wVmax = 0; | 
|  | lpCaps->wCaps = 0; | 
|  | lpCaps->wMaxAxes = 2; | 
|  | lpCaps->wNumAxes = 2; | 
|  | lpCaps->wMaxButtons = 4; | 
|  | lpCaps->szRegKey[0] = 0; | 
|  | lpCaps->szOEMVxD[0] = 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return JOYERR_NOERROR; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				JSTCK_GetPos			[internal] | 
|  | */ | 
|  | static LRESULT JSTCK_GetPosEx(DWORD_PTR dwDevID, LPJOYINFOEX lpInfo) | 
|  | { | 
|  | WINE_JSTCK*		jstck; | 
|  | int			dev; | 
|  | #ifdef HAVE_LINUX_22_JOYSTICK_API | 
|  | struct js_event 	ev; | 
|  | #else | 
|  | struct js_status 	js; | 
|  | int    		dev_stat; | 
|  | #endif | 
|  |  | 
|  | if ((jstck = JSTCK_drvGet(dwDevID)) == NULL) | 
|  | return MMSYSERR_NODRIVER; | 
|  |  | 
|  | if ((dev = JSTCK_OpenDevice(jstck)) < 0) return JOYERR_PARMS; | 
|  |  | 
|  | #ifdef HAVE_LINUX_22_JOYSTICK_API | 
|  | while ((read(dev, &ev, sizeof(struct js_event))) > 0) { | 
|  | if (ev.type == (JS_EVENT_AXIS)) { | 
|  | switch (ev.number) { | 
|  | case 0: | 
|  | jstck->x = ev.value; | 
|  | break; | 
|  | case 1: | 
|  | jstck->y = ev.value; | 
|  | break; | 
|  | case 2: | 
|  | jstck->z = ev.value; | 
|  | break; | 
|  | case 3: | 
|  | jstck->r = ev.value; | 
|  | break; | 
|  | case 4: | 
|  | jstck->u = ev.value; | 
|  | break; | 
|  | case 5: | 
|  | jstck->v = ev.value; | 
|  | break; | 
|  | default: | 
|  | FIXME("Unknown joystick event '%d'\n", ev.number); | 
|  | } | 
|  | } else if (ev.type == (JS_EVENT_BUTTON)) { | 
|  | if (ev.value) { | 
|  | jstck->buttons |= (1 << ev.number); | 
|  | /* FIXME: what to do for this field when | 
|  | * multiple buttons are depressed ? | 
|  | */ | 
|  | if (lpInfo->dwFlags & JOY_RETURNBUTTONS) | 
|  | lpInfo->dwButtonNumber = ev.number + 1; | 
|  | } | 
|  | else | 
|  | jstck->buttons  &= ~(1 << ev.number); | 
|  | } | 
|  | } | 
|  | /* EAGAIN is returned when the queue is empty */ | 
|  | if (errno != EAGAIN) { | 
|  | /* FIXME: error should not be ignored */ | 
|  | ERR("Error while reading joystick state (%s)\n", strerror(errno)); | 
|  | } | 
|  | /* Now, copy the cached values into Window's structure... */ | 
|  | if (lpInfo->dwFlags & JOY_RETURNBUTTONS) | 
|  | lpInfo->dwButtons = jstck->buttons; | 
|  | if (lpInfo->dwFlags & JOY_RETURNX) | 
|  | lpInfo->dwXpos   = jstck->x + 32767; | 
|  | if (lpInfo->dwFlags & JOY_RETURNY) | 
|  | lpInfo->dwYpos   = jstck->y + 32767; | 
|  | if (lpInfo->dwFlags & JOY_RETURNZ) | 
|  | lpInfo->dwZpos   = jstck->z + 32767; | 
|  | if (lpInfo->dwFlags & JOY_RETURNR) | 
|  | lpInfo->dwRpos   = jstck->r + 32767; | 
|  | # ifdef BODGE_THE_HAT | 
|  | else if (lpInfo->dwFlags & JOY_RETURNBUTTONS) | 
|  | { | 
|  | if (jstck->r > 0) | 
|  | lpInfo->dwButtons |= 1<<7; | 
|  | else if (jstck->r < 0) | 
|  | lpInfo->dwButtons |= 1<<8; | 
|  | } | 
|  | # endif | 
|  | if (lpInfo->dwFlags & JOY_RETURNU) | 
|  | lpInfo->dwUpos   = jstck->u + 32767; | 
|  | # ifdef BODGE_THE_HAT | 
|  | else if (lpInfo->dwFlags & JOY_RETURNBUTTONS) | 
|  | { | 
|  | if (jstck->u > 0) | 
|  | lpInfo->dwButtons |= 1<<9; | 
|  | else if (jstck->u < 0) | 
|  | lpInfo->dwButtons |= 1<<10; | 
|  | } | 
|  | # endif | 
|  | if (lpInfo->dwFlags & JOY_RETURNV) | 
|  | lpInfo->dwVpos   = jstck->v + 32767; | 
|  |  | 
|  | #else | 
|  | dev_stat = read(dev, &js, sizeof(js)); | 
|  | if (dev_stat != sizeof(js)) { | 
|  | return JOYERR_UNPLUGGED; /* FIXME: perhaps wrong, but what should I return else ? */ | 
|  | } | 
|  | js.x = js.x<<8; | 
|  | js.y = js.y<<8; | 
|  | if (lpInfo->dwFlags & JOY_RETURNX) | 
|  | lpInfo->dwXpos = js.x;   /* FIXME: perhaps multiply it somehow ? */ | 
|  | if (lpInfo->dwFlags & JOY_RETURNY) | 
|  | lpInfo->dwYpos = js.y; | 
|  | if (lpInfo->dwFlags & JOY_RETURNBUTTONS) | 
|  | lpInfo->dwButtons = js.buttons; | 
|  | #endif | 
|  |  | 
|  | TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, flags: 0x%04x (fd %d)\n", | 
|  | lpInfo->dwXpos, lpInfo->dwYpos, lpInfo->dwZpos, | 
|  | lpInfo->dwRpos, lpInfo->dwUpos, lpInfo->dwVpos, | 
|  | (unsigned int)lpInfo->dwButtons, | 
|  | (unsigned int)lpInfo->dwFlags, | 
|  | dev | 
|  | ); | 
|  |  | 
|  | return JOYERR_NOERROR; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				JSTCK_GetPos			[internal] | 
|  | */ | 
|  | static LRESULT JSTCK_GetPos(DWORD_PTR dwDevID, LPJOYINFO lpInfo) | 
|  | { | 
|  | JOYINFOEX	ji; | 
|  | LONG	ret; | 
|  |  | 
|  | memset(&ji, 0, sizeof(ji)); | 
|  |  | 
|  | ji.dwSize = sizeof(ji); | 
|  | ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS; | 
|  | ret = JSTCK_GetPosEx(dwDevID, &ji); | 
|  | if (ret == JOYERR_NOERROR)	{ | 
|  | lpInfo->wXpos    = ji.dwXpos; | 
|  | lpInfo->wYpos    = ji.dwYpos; | 
|  | lpInfo->wZpos    = ji.dwZpos; | 
|  | lpInfo->wButtons = ji.dwButtons; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				DriverProc (JOYSTICK.@) | 
|  | */ | 
|  | LRESULT CALLBACK JSTCK_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, | 
|  | LPARAM dwParam1, LPARAM dwParam2) | 
|  | { | 
|  | /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */ | 
|  | /* EPP 	  dwDevID, hDriv, wMsg, dwParam1, dwParam2); */ | 
|  |  | 
|  | switch(wMsg) { | 
|  | case DRV_LOAD:		return 1; | 
|  | case DRV_FREE:		return 1; | 
|  | case DRV_OPEN:		return JSTCK_drvOpen((LPSTR)dwParam1, dwParam2); | 
|  | case DRV_CLOSE:		return JSTCK_drvClose(dwDevID); | 
|  | case DRV_ENABLE:		return 1; | 
|  | case DRV_DISABLE:		return 1; | 
|  | case DRV_QUERYCONFIGURE:	return 1; | 
|  | case DRV_CONFIGURE:		MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);	return 1; | 
|  | case DRV_INSTALL:		return DRVCNF_RESTART; | 
|  | case DRV_REMOVE:		return DRVCNF_RESTART; | 
|  |  | 
|  | case JDD_GETNUMDEVS:	return 1; | 
|  | case JDD_GETDEVCAPS:	return JSTCK_GetDevCaps(dwDevID, (LPJOYCAPSW)dwParam1, dwParam2); | 
|  | case JDD_GETPOS:		return JSTCK_GetPos(dwDevID, (LPJOYINFO)dwParam1); | 
|  | case JDD_SETCALIBRATION: | 
|  | case JDD_CONFIGCHANGED:	return JOYERR_NOCANDO; | 
|  | case JDD_GETPOSEX:		return JSTCK_GetPosEx(dwDevID, (LPJOYINFOEX)dwParam1); | 
|  | default: | 
|  | return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); | 
|  | } | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				DriverProc (JOYSTICK.@) | 
|  | */ | 
|  | LRESULT CALLBACK JSTCK_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, | 
|  | LPARAM dwParam1, LPARAM dwParam2) | 
|  | { | 
|  | /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */ | 
|  | /* EPP 	  dwDevID, hDriv, wMsg, dwParam1, dwParam2); */ | 
|  |  | 
|  | switch(wMsg) { | 
|  | case DRV_LOAD: | 
|  | case DRV_FREE: | 
|  | case DRV_OPEN: | 
|  | case DRV_CLOSE: | 
|  | case DRV_ENABLE: | 
|  | case DRV_DISABLE: | 
|  | case DRV_QUERYCONFIGURE:	return 0; | 
|  | case DRV_CONFIGURE:		MessageBoxA(0, "JoyStick MultiMedia Driver !", "JoyStick Driver", MB_OK);	return 1; | 
|  | case DRV_INSTALL:		return DRVCNF_RESTART; | 
|  | case DRV_REMOVE:		return DRVCNF_RESTART; | 
|  | default: | 
|  | return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif |