- Fix the tics drawing code.
[wine] / msdos / ppdev.c
1 /*
2  * Parallel-port device support
3  *
4  * Copyright 2001 Uwe Bonnes
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #ifdef HAVE_PPDEV
24
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #ifdef HAVE_SYS_IOCTL_H
30 # include <sys/ioctl.h>
31 #endif
32 #include <errno.h>
33 #include <linux/ppdev.h>
34
35 #include "winerror.h"
36 #include "winreg.h"
37
38 #include "miscemu.h"
39
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(int);
43
44 typedef struct _PPDEVICESTRUCT{
45   int fd; /* NULL if device not available */
46   char *devicename;
47   int userbase; /* where wine thinks the ports are*/
48   DWORD lastaccess; /* or NULL if release */
49   int timeout; /* time in second of inactivity to release the port*/
50 } PPDeviceStruct;
51
52 static PPDeviceStruct PPDeviceList[5];
53 static int PPDeviceNum=0;
54
55 static int IO_pp_sort(const void *p1,const  void *p2)
56 {
57   return ((PPDeviceStruct*)p1)->userbase - ((PPDeviceStruct*)p2)->userbase;
58 }
59
60 /* IO_pp_init
61  *
62  * Read the ppdev entries from wine.conf, open the device and check
63  * for nescessary IOCTRL
64  * Report verbose about possible errors
65  */
66 char IO_pp_init(void)
67 {
68     char name[80];
69     char buffer[1024];
70     HKEY hkey;
71     char temp[256];
72     int i,idx=0,fd,res,userbase,nports=0;
73     char * timeout;
74     char ret=1;
75     int lasterror;
76
77     TRACE("\n");
78     if (RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ppdev", &hkey ) != ERROR_SUCCESS)
79       return 1;
80
81     for (;;)
82     {
83         DWORD type, count = sizeof(buffer), name_len = sizeof(name);
84
85         if (RegEnumValueA( hkey, idx, name, &name_len, NULL, &type, buffer, &count )!= ERROR_SUCCESS)
86           break;
87
88         idx++;
89         if(nports >4)
90           {
91             FIXME("Make the PPDeviceList larger then 5 elements\n");
92             break;
93           }
94         TRACE("Device '%s' at virtual userbase '%s'\n", buffer,name);
95         timeout = strchr(buffer,',');
96         if (timeout)
97           *timeout++=0;
98         fd=open(buffer,O_RDWR);
99         lasterror=errno;
100         if (fd == -1)
101           {
102             WARN("Configuration: No access to %s Cause: %s\n",buffer,strerror(lasterror));
103             WARN("Rejecting configuration item\n");
104             if (lasterror == ENODEV)
105               FIXME("Is the ppdev module loaded?\n");
106             continue;
107           }
108         userbase = strtol(name,(char **)NULL, 16);
109         if ( errno == ERANGE)
110           {
111             WARN("Configuration: Invalid base %s for %s\n",name,buffer);
112             WARN("Rejecting configuration item\n");
113             continue;
114           }
115         if (ioctl (fd,PPCLAIM,0))
116           {
117             ERR("PPCLAIM rejected %s\n",buffer);
118             ERR("Perhaps the device is already in use or non-existant\n");
119             continue;
120           }
121         if (nports > 0)
122           {
123             for (i=0; i<= nports; i++)
124               {
125                 if (PPDeviceList[i].userbase == userbase)
126                   {
127                     WARN("Configuration: %s uses the same virtual ports as %s\n",
128                          buffer,PPDeviceList[0].devicename);
129                     WARN("Configuration: Rejecting configuration item\n");
130                     userbase = 0;
131                     break;
132                   }
133               }
134             if (!userbase) continue;
135           }
136         /* Check for the minimum required IOCTLS */
137         if ((ioctl(fd,PPRDATA,&res))||
138             (ioctl(fd,PPRCONTROL,&res))||
139             (ioctl(fd,PPRCONTROL,&res)))
140           {
141             ERR("PPUSER IOCTL not available for parport device %s\n",temp);
142             continue;
143           }
144         if (ioctl (fd,PPRELEASE,0))
145           {
146             ERR("PPRELEASE rejected %s\n",buffer);
147             ERR("Perhaps the device is already in use or non-existant\n");
148             continue;
149           }
150         PPDeviceList[nports].devicename = malloc(sizeof(buffer)+1);
151         if (!PPDeviceList[nports].devicename)
152           {
153             ERR("No (more)space for devicename\n");
154             break;
155           }
156         strcpy(PPDeviceList[nports].devicename,buffer);
157         PPDeviceList[nports].fd = fd;
158         PPDeviceList[nports].userbase = userbase;
159         PPDeviceList[nports].lastaccess=GetTickCount();
160         if (timeout)
161           {
162             PPDeviceList[nports].timeout = strtol(timeout,(char **)NULL, 10);
163             if (errno == ERANGE)
164               {
165                 WARN("Configuration:Invalid timeout %s in configuration for %s, Setting to 0\n",
166                      timeout,buffer);
167                 PPDeviceList[nports].timeout = 0;
168               }
169           }
170         else
171           PPDeviceList[nports].timeout = 0;
172         nports++;
173     }
174     TRACE("found %d ports\n",nports);
175     RegCloseKey( hkey );
176
177     PPDeviceNum= nports;
178     if (nports > 1)
179       /* sort in accending order for userbase for faster access*/
180       qsort (PPDeviceList,PPDeviceNum,sizeof(PPDeviceStruct),IO_pp_sort);
181
182     if (nports)
183       ret=0;
184     for (idx= 0;idx<PPDeviceNum; idx++)
185       TRACE("found device %s userbase %x fd %x timeout %d\n",
186             PPDeviceList[idx].devicename, PPDeviceList[idx].userbase,
187             PPDeviceList[idx].fd,PPDeviceList[idx].timeout);
188     /* FIXME:
189        register a timer callback perhaps every 30 second to release unused ports
190        Set lastaccess = 0 as indicator when port was released
191     */
192     return ret;
193 }
194
195 /* IO_pp_do_access
196  *
197  * Do the actual IOCTL
198  * Return NULL on success
199  */
200 static int IO_pp_do_access(int idx,int ppctl, DWORD* res)
201 {
202   int ret;
203   if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0))
204     {
205       ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
206           PPDeviceList[idx].devicename);
207       return 1;
208     }
209   ret = ioctl(PPDeviceList[idx].fd,ppctl,res);
210   if (ioctl(PPDeviceList[idx].fd,PPRELEASE,0))
211     {
212       ERR("Can't release device %s, PPUSER/PPDEV handling confused\n",
213           PPDeviceList[idx].devicename);
214       return 1;
215     }
216   return ret;
217
218 }
219
220 /* IO_pp_inp
221  *
222  * Check if we can satisfy the INP command with some of the configured PPDEV deviced
223  * Return NULL on success
224  */
225 int IO_pp_inp(int port, DWORD* res)
226 {
227     int idx,j=0;
228
229     for (idx=0;idx<PPDeviceNum ;idx++)
230       {
231        j = port - PPDeviceList[idx].userbase;
232        if (j <0) return 1;
233        switch (j)
234          {
235          case 0:
236            return IO_pp_do_access(idx,PPRDATA,res);
237          case 1:
238            return IO_pp_do_access(idx,PPRSTATUS,res);
239          case 2:
240            return IO_pp_do_access(idx,PPRCONTROL,res);
241          case 0x400:
242          case 0x402:
243          case 3:
244          case 4:
245          case 0x401:
246            FIXME("Port 0x%x not accessible for reading with ppdev\n",port);
247            FIXME("If this is causing problems, try direct port access\n");
248            return 1;
249          default:
250            break;
251          }
252       }
253     return 1;
254 }
255
256 /* IO_pp_outp
257  *
258  * Check if we can satisfy the INP command with some of the configured PPDEV deviced
259  * Return NULL on success
260  */
261 BOOL IO_pp_outp(int port, DWORD* res)
262 {
263     int idx,j=0;
264
265     for (idx=0;idx<PPDeviceNum ;idx++)
266       {
267        j = port - PPDeviceList[idx].userbase;
268        if (j <0) return 1;
269        switch (j)
270          {
271          case 0:
272            return IO_pp_do_access(idx,PPWDATA,res);
273          case 2:
274            return IO_pp_do_access(idx,PPWCONTROL,res);
275          case 1:
276          case 0x400:
277          case 0x402:
278          case 3:
279          case 4:
280          case 0x401:
281            FIXME("Port %d not accessible for writing with ppdev\n",port);
282            FIXME("If this is causing problems, try direct port access\n");
283            return 1;
284          default:
285            break;
286          }
287       }
288     return TRUE;
289 }
290
291
292 #else /* HAVE_PPDEV */
293
294 #include "windef.h"
295
296 char IO_pp_init(void)
297 {
298   return 1;
299 }
300
301 int IO_pp_inp(int port, DWORD* res)
302 {
303   return 1;
304 }
305
306 BOOL IO_pp_outp(int port, DWORD* res)
307 {
308   return TRUE;
309 }
310 #endif  /* HAVE_PPDEV */