|  | /* | 
|  | * Parallel-port device support | 
|  | * | 
|  | * Copyright 2001 Uwe Bonnes | 
|  | * | 
|  | * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #ifdef HAVE_PPDEV | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <fcntl.h> | 
|  | #ifdef HAVE_SYS_IOCTL_H | 
|  | # include <sys/ioctl.h> | 
|  | #endif | 
|  | #include <errno.h> | 
|  | #include <linux/ppdev.h> | 
|  |  | 
|  | #include "winerror.h" | 
|  | #include "winreg.h" | 
|  |  | 
|  | #include "miscemu.h" | 
|  |  | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(int); | 
|  |  | 
|  | 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,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\n"); | 
|  | 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; | 
|  | } | 
|  | if (ioctl (fd,PPRELEASE,0)) | 
|  | { | 
|  | ERR("PPRELEASE rejected %s\n",buffer); | 
|  | ERR("Perhaps the device is already in use or non-existant\n"); | 
|  | 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); | 
|  | RegCloseKey( hkey ); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | int ret; | 
|  | if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0)) | 
|  | { | 
|  | ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n", | 
|  | PPDeviceList[idx].devicename); | 
|  | return 1; | 
|  | } | 
|  | ret = ioctl(PPDeviceList[idx].fd,ppctl,res); | 
|  | if (ioctl(PPDeviceList[idx].fd,PPRELEASE,0)) | 
|  | { | 
|  | ERR("Can't release device %s, PPUSER/PPDEV handling confused\n", | 
|  | PPDeviceList[idx].devicename); | 
|  | return 1; | 
|  | } | 
|  | return ret; | 
|  |  | 
|  | } | 
|  |  | 
|  | /* 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 */ | 
|  |  | 
|  | #include "windef.h" | 
|  |  | 
|  | 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  /* HAVE_PPDEV */ |