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