Use /dev/parport for direct port accessed

Uwe Bonnes bon at elektron.ikp.physik.tu-darmstadt.de
Thu Jun 21 07:56:24 CDT 2001


Hallo,

some programs run with so called dongles on the parallel port for copy
protection. To allow such program to access the parallel port with inb() and
outb(), wine needs to be run as as root. The linux 2.4 kernel contains the
ppdev driver for char-major 99 (/dev/parport) and some kind of direct
accesses to the parallel port might be done through this driver. As only
access to the parport device is needed, a user with access rights to the
device may run the program.

Appended patch translates inb() and outb() accesses to the parallel port
device to the appropriate ppdev ioctl(). It allows to run the dongle
protected Quicklogic program "spde" and to recognize the license. The patch
checks for the needed ioctl() during compile and run-time. Run "autoconf"
and "autoheader -l include" after applying the patch.

For this to work you need:
- a kernel with ppdev compiled in or as module
- in latter case the module loaded or 
  "alias char-major-99       ppdev" in /etc/modules.conf
- /dev/parport entries as in /usr/scr/Documentation/devices.txt
- Read/Write access to the device

Beware: The patch contains the patch "Initializing Dosvm for direct port
access" sent to wine-devel for discussion.

Bye
-- 
Uwe Bonnes                bon at elektron.ikp.physik.tu-darmstadt.de

Institut fuer Kernphysik  Schlossgartenstrasse 9  64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------

Index: wine/configure.in
===================================================================
RCS file: /home/wine/wine/configure.in,v
retrieving revision 1.209
diff -u -r1.209 configure.in
--- wine/configure.in	2001/06/19 03:30:14	1.209
+++ wine/configure.in	2001/06/21 12:35:36
@@ -430,6 +430,19 @@
 AC_SUBST(FREETYPELIBS)
 AC_SUBST(FREETYPEINCL)
 
+dnl **** Check for parport (currently Linux only) ****
+AC_CACHE_CHECK("for parport header/ppdev.h", ac_cv_c_ppdev,
+ AC_TRY_COMPILE(
+   [#include <linux/ppdev.h>],
+   [ioctl (1,PPCLAIM,0)],
+   [ac_cv_c_ppdev="yes"],
+   [ac_cv_c_ppdev="no"])
+ )
+if test "$ac_cv_c_ppdev" = "yes"
+then
+    AC_DEFINE(HAVE_PPDEV)
+fi
+
 dnl **** Check for IPX (currently Linux only) ****
 AC_CACHE_CHECK("for GNU style IPX support", ac_cv_c_ipx_gnu,
  AC_TRY_COMPILE(
Index: wine/include/acconfig.h
===================================================================
RCS file: /home/wine/wine/include/acconfig.h,v
retrieving revision 1.41
diff -u -r1.41 acconfig.h
--- wine/include/acconfig.h	2001/06/08 19:34:57	1.41
+++ wine/include/acconfig.h	2001/06/21 12:35:36
@@ -87,6 +87,9 @@
 /* Define if we can a compatible xterm program */
 #undef XTERM_PROGRAM
 
+/* Define if we have ppdev.h for parport access */
+#undef HAVE_PPDEV
+
 /* Define if IPX should use netipx/ipx.h from libc */
 #undef HAVE_IPX_GNU
 
Index: wine/include/miscemu.h
===================================================================
RCS file: /home/wine/wine/include/miscemu.h,v
retrieving revision 1.41
diff -u -r1.41 miscemu.h
--- wine/include/miscemu.h	2000/12/26 00:22:46	1.41
+++ wine/include/miscemu.h	2001/06/21 12:35:36
@@ -249,6 +249,12 @@
 /* misc/aspi.c */
 extern void ASPI_DOS_HandleInt(CONTEXT86 *context);
 
+/* misc/ppdev.c */
+
+extern BOOL IO_pp_outp(int port, DWORD* res);
+extern int IO_pp_inp(int port, DWORD* res);
+extern char IO_pp_init(void);
+
 #define PTR_REAL_TO_LIN(seg,off) \
    ((void*)(((unsigned int)(seg) << 4) + LOWORD(off)))
 
Index: wine/documentation/samples/config
===================================================================
RCS file: /home/wine/wine/documentation/samples/config,v
retrieving revision 1.12
diff -u -r1.12 config
--- wine/documentation/samples/config	2001/05/22 19:19:50	1.12
+++ wine/documentation/samples/config	2001/06/21 12:35:36
@@ -167,6 +167,14 @@
 [parallelports]
 "Lpt1" = "/dev/lp0"
 
+[ppdev]
+;; key:  io-base of the emulated port
+;; value : parport-device{,timeout}
+;; timeout for auto closing an open device ( not yet implemented)
+;3bc=/dev/parport2
+"378" = "/dev/parport0"
+;278=/dev/parport1
+
 [spooler]
 "FILE:" = "tmp.ps"
 "LPT1:" = "|lpr"
Index: wine/msdos/Makefile.in
===================================================================
RCS file: /home/wine/wine/msdos/Makefile.in,v
retrieving revision 1.12
diff -u -r1.12 Makefile.in
--- wine/msdos/Makefile.in	2000/09/29 00:28:33	1.12
+++ wine/msdos/Makefile.in	2001/06/21 12:35:36
@@ -36,6 +36,7 @@
 	int5c.c \
 	interrupts.c \
 	ioports.c \
+	ppdev.c \
 	vga.c \
 	vxd.c
 
Index: wine/msdos/ioports.c
===================================================================
RCS file: /home/wine/wine/msdos/ioports.c,v
retrieving revision 1.24
diff -u -r1.24 ioports.c
--- wine/msdos/ioports.c	2001/05/09 17:31:35	1.24
+++ wine/msdos/ioports.c	2001/06/21 12:35:36
@@ -62,13 +62,17 @@
 # define DIRECT_IO_ACCESS
 #else
 # undef DIRECT_IO_ACCESS
+# undef PP_IO_ACCESS
 #endif  /* linux && __i386__ */
 
 #ifdef DIRECT_IO_ACCESS
 
 extern int iopl(int level);
-
 static char do_direct_port_access = -1;
+#ifdef HAVE_PPDEV
+static char do_pp_port_access = -1; /* -1: uninitialized, 1: not available
+				       0: available);*/
+#endif
 static char port_permissions[0x10000];
 
 #define IO_READ  1
@@ -231,6 +235,7 @@
 
         do_direct_port_access = 0;
 	/* Can we do that? */
+	DPMI_LoadDosSystem();
 	if (!iopl(3)) {
 		iopl(0);
 
@@ -258,6 +263,13 @@
 
     TRACE("%d-byte value from port 0x%02x\n", size, port );
 
+#ifdef HAVE_PPDEV
+    if (do_pp_port_access == -1) 
+      do_pp_port_access =IO_pp_init();
+    if ((do_pp_port_access == 0 ) && (size == 1))
+      if (!IO_pp_inp(port,&res))
+         return res;
+#endif
 #ifdef DIRECT_IO_ACCESS
     if (do_direct_port_access == -1) IO_port_init();
     if ((do_direct_port_access)
@@ -371,7 +383,15 @@
     TRACE("IO: 0x%lx (%d-byte value) to port 0x%02x\n",
                  value, size, port );
 
+#ifdef HAVE_PPDEV
+    if (do_pp_port_access == -1) 
+      do_pp_port_access = IO_pp_init();
+    if ((do_pp_port_access == 0) && (size == 1))
+      if (!IO_pp_outp(port,&value))
+         return;
+#endif
 #ifdef DIRECT_IO_ACCESS
+
     if (do_direct_port_access == -1) IO_port_init();
     if ((do_direct_port_access)
         /* Make sure we have access to the port */
--- /dev/null	Tue Feb 20 18:39:20 2001
+++ wine/msdos/ppdev.c	Thu Jun 21 14:32:14 2001
@@ -0,0 +1,283 @@
+#include "config.h"
+
+#include "debugtools.h"
+
+DEFAULT_DEBUG_CHANNEL(int);
+
+#ifdef  HAVE_PPDEV
+
+#include <stdlib.h>
+#include <sys/types.h> 
+#include <sys/stat.h>  
+#include <fcntl.h>     
+#include <sys/ioctl.h> 
+#include <errno.h>     
+#include <linux/ppdev.h> 
+
+#include "winerror.h"
+#include "winreg.h"
+    
+
+typedef struct _PPDEVICESTRUCT{
+  int fd; /* NULL if device not available */
+  char *devicename;
+  int userbase; /* where wine thinks the ports are*/
+  DWORD lastaccess; /* or NULL if release */
+  int timeout; /* time in second of inactivity to release the port*/
+} PPDeviceStruct;
+
+static PPDeviceStruct PPDeviceList[5];
+static int PPDeviceNum=0;
+
+static int IO_pp_sort(const void *p1,const  void *p2)
+{
+  return ((PPDeviceStruct*)p1)->userbase - ((PPDeviceStruct*)p2)->userbase;
+}
+
+/* IO_pp_init 
+ *
+ * Read the ppdev entries from wine.conf, open the device and check
+ * for nescessary IOCTRL
+ * Report verbose about possible errors
+ */
+char IO_pp_init(void)
+{
+    char name[80];
+    char buffer[1024];
+    HKEY hkey;
+    char temp[256];
+    int i,idx=0,fd,res,userbase,nports=0;
+    char * timeout;
+    char ret=1;
+    int lasterror;
+
+    TRACE("\n");
+    if (RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ppdev", &hkey ) != ERROR_SUCCESS)
+      return 1;
+
+    for (;;)
+    {
+        DWORD type, count = sizeof(buffer), name_len = sizeof(name);
+
+        if (RegEnumValueA( hkey, idx, name, &name_len, NULL, &type, buffer, &count )!= ERROR_SUCCESS) 
+	  break;
+	
+	idx++;
+	if(nports >4)
+	  {
+	    FIXME("Make the PPDeviceList larger then 5 elements\n");
+	    break;
+	  }
+	TRACE("Device '%s' at virtual userbase '%s'\n", buffer,name);
+	timeout = strchr(buffer,',');
+	if (timeout)
+	  *timeout++=0;
+	fd=open(buffer,O_RDWR);
+	lasterror=errno;
+	if (fd == -1)
+	  {
+	    WARN("Configuration: No access to %s Cause: %s\n",buffer,strerror(lasterror));
+	    WARN("Rejecting configuration item\n");
+	    if (lasterror == ENODEV)
+	      FIXME("Is the ppdev module loaded?\n");
+	    continue;
+	  }
+	userbase = strtol(name,(char **)NULL, 16);
+	if ( errno == ERANGE)
+	  {
+	    WARN("Configuration: Invalid base %s for %s\n",name,buffer);
+	    WARN("Rejecting configuration item\n");
+	    continue;
+	  }
+	if (ioctl (fd,PPEXCL,0)) 
+	  {
+	    ERR("PPEXCL IOCTL rejected for device %s\n",buffer);
+	    ERR("Check that PPDEV module is loaded!\n");
+	    ERR("Perhaps the device is already in use or non-existant\n");
+	    continue;
+	  }
+	if (ioctl (fd,PPCLAIM,0)) 
+	  {
+	    ERR("PPCLAIM rejected %s\n",buffer);
+	    ERR("Perhaps the device is already in use or non-existant\n");
+	    continue;
+	  }
+	if (nports > 0)
+	  {
+	    for (i=0; i<= nports; i++)
+	      {
+		if (PPDeviceList[i].userbase == userbase)
+		  {
+		    WARN("Configuration: %s uses the same virtual ports as %s\n",
+			 buffer,PPDeviceList[0].devicename);
+		    WARN("Configuration: Rejecting configuration item");
+		    userbase = 0;
+		    break;
+		  }
+	      }
+	    if (!userbase) continue;
+	  }
+	/* Check for the minimum required IOCTLS */
+	if ((ioctl(fd,PPRDATA,&res))||
+	    (ioctl(fd,PPRCONTROL,&res))||
+	    (ioctl(fd,PPRCONTROL,&res)))
+	  {
+	    ERR("PPUSER IOCTL not available for parport device %s\n",temp);
+	    continue;
+	  }
+	PPDeviceList[nports].devicename = malloc(sizeof(buffer)+1);
+	if (!PPDeviceList[nports].devicename)
+	  {
+	    ERR("No (more)space for devicename\n");
+	    break;
+	  }
+	strcpy(PPDeviceList[nports].devicename,buffer);
+	PPDeviceList[nports].fd = fd;
+	PPDeviceList[nports].userbase = userbase;
+	PPDeviceList[nports].lastaccess=GetTickCount();
+	if (timeout)
+	  {
+	    PPDeviceList[nports].timeout = strtol(timeout,(char **)NULL, 10);
+	    if (errno == ERANGE)
+	      {
+		WARN("Configuration:Invalid timeout %s in configuration for %s, Setting to 0\n",
+		     timeout,buffer);
+		PPDeviceList[nports].timeout = 0;
+	      }
+	  }
+	else
+	  PPDeviceList[nports].timeout = 0;
+	nports++;
+    }
+    TRACE("found %d ports\n",nports);
+    
+    PPDeviceNum= nports;
+    if (nports > 1)
+      /* sort in accending order for userbase for faster access*/
+      qsort (PPDeviceList,PPDeviceNum,sizeof(PPDeviceStruct),IO_pp_sort);
+    
+    if (nports)
+      ret=0;
+    for (idx= 0;idx<PPDeviceNum; idx++)
+      TRACE("found device %s userbase %x fd %x timeout %d\n",
+	    PPDeviceList[idx].devicename, PPDeviceList[idx].userbase,
+	    PPDeviceList[idx].fd,PPDeviceList[idx].timeout);
+    /* FIXME:
+       register a timer callback perhaps every 30 second to release unused ports
+       Set lastaccess = 0 as indicator when port was released
+    */
+    return ret;
+}
+
+/* IO_pp_do_access
+ *
+ * Do the actual IOCTL
+ * Return NULL on success
+ */
+static int IO_pp_do_access(int idx,int ppctl, DWORD* res)
+{
+    if (!PPDeviceList[idx].lastaccess)
+      /* TIMER callback has released the device after some time of inactivity
+          Reclaim the device
+          !!!!!!THIS MAY UNINTERRUPTIPLE BLOCK IF SOME OTHER DEVICE
+           !!!!!! HAS CLAIMED THE SAME PORT
+      */
+      if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0))
+       {
+         ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
+             PPDeviceList[idx].devicename);
+         return 1;
+       }
+    PPDeviceList[idx].lastaccess=GetTickCount();
+    return ioctl(PPDeviceList[idx].fd,ppctl,res);
+}
+
+/* IO_pp_inp
+ *
+ * Check if we can satisfy the INP command with some of the configured PPDEV deviced
+ * Return NULL on success
+ */
+int IO_pp_inp(int port, DWORD* res)
+{
+    int idx,j=0;
+    
+    for (idx=0;idx<PPDeviceNum ;idx++)
+      {
+       j = port - PPDeviceList[idx].userbase;
+       if (j <0) return 1;
+       switch (j)
+         {
+         case 0:
+           return IO_pp_do_access(idx,PPRDATA,res);
+         case 1:
+           return IO_pp_do_access(idx,PPRSTATUS,res);
+         case 2:
+           return IO_pp_do_access(idx,PPRCONTROL,res);
+         case 0x400:
+         case 0x402:
+         case 3:
+         case 4:
+         case 0x401:
+           FIXME("Port 0x%x not accessible for reading with ppdev\n",port);
+           FIXME("If this is causing problems, try direct port access\n");
+           return 1;
+         default:
+           break;
+         }
+      }
+    return 1;
+}
+
+/* IO_pp_outp
+ *
+ * Check if we can satisfy the INP command with some of the configured PPDEV deviced
+ * Return NULL on success
+ */
+BOOL IO_pp_outp(int port, DWORD* res)
+{
+    int idx,j=0;
+    
+    for (idx=0;idx<PPDeviceNum ;idx++)
+      {
+       j = port - PPDeviceList[idx].userbase;
+       if (j <0) return 1;
+       switch (j)
+         {
+         case 0:
+           return IO_pp_do_access(idx,PPWDATA,res);
+         case 2:
+           return IO_pp_do_access(idx,PPWCONTROL,res);
+         case 1:
+         case 0x400:
+         case 0x402:
+         case 3:
+         case 4:
+         case 0x401:
+           FIXME("Port %d not accessible for writing with ppdev\n",port);
+           FIXME("If this is causing problems, try direct port access\n");
+           return 1;
+         default:
+           break;
+         }
+      }
+    return TRUE;
+}
+
+
+#else /* HAVE_PPDEV */
+
+char IO_pp_init(void)
+{
+  return 1;
+}
+
+int IO_pp_inp(int port, DWORD* res)
+{
+  return 1;
+}
+
+BOOL IO_pp_outp(int port, DWORD* res)
+{
+  return TRUE;
+}
+#endif




More information about the wine-patches mailing list