advapi32/tests: Make sure NT4 and W2K tests don't fail.
[wine] / dlls / winedos / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include "windef.h"
25
26 #ifdef HAVE_PPDEV
27
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #ifdef HAVE_SYS_STAT_H
32 # include <sys/stat.h>
33 #endif
34 #include <fcntl.h>
35 #include <errno.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 # include <sys/ioctl.h>
38 #endif
39 #ifdef HAVE_LINUX_IOCTL_H
40 # include <linux/ioctl.h>
41 #endif
42 #include <linux/ppdev.h>
43
44 #include "winerror.h"
45 #include "winbase.h"
46 #include "winreg.h"
47 #include "winternl.h"
48
49 #include "wine/debug.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(int);
52
53 typedef struct _PPDEVICESTRUCT{
54   int fd; /* NULL if device not available */
55   char *devicename;
56   int userbase; /* where wine thinks the ports are */
57   DWORD lastaccess; /* or NULL if release */
58   int timeout; /* time in second of inactivity to release the port */
59 } PPDeviceStruct;
60
61 static PPDeviceStruct PPDeviceList[5];
62 static int PPDeviceNum=0;
63
64 static int IO_pp_sort(const void *p1,const  void *p2)
65 {
66     return ((const PPDeviceStruct*)p1)->userbase - ((const PPDeviceStruct*)p2)->userbase;
67 }
68
69 /* IO_pp_init
70  *
71  * Read the ppdev entries from wine.conf, open the device and check
72  * for necessary IOCTRL
73  * Report verbose about possible errors
74  */
75 char IO_pp_init(void)
76 {
77     char name[80];
78     char buffer[256];
79     HANDLE root, hkey;
80     int i,idx=0,fd,res,userbase,nports=0;
81     char * timeout;
82     char ret=1;
83     int lasterror;
84     OBJECT_ATTRIBUTES attr;
85     UNICODE_STRING nameW;
86
87     static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\',
88                                     'W','i','n','e','\\','V','D','M','\\','p','p','d','e','v',0};
89
90     TRACE("\n");
91
92     RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
93     attr.Length = sizeof(attr);
94     attr.RootDirectory = root;
95     attr.ObjectName = &nameW;
96     attr.Attributes = 0;
97     attr.SecurityDescriptor = NULL;
98     attr.SecurityQualityOfService = NULL;
99     RtlInitUnicodeString( &nameW, configW );
100
101     /* @@ Wine registry key: HKCU\Software\Wine\VDM\ppdev */
102     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
103     NtClose( root );
104     if (!hkey) return 1;
105
106     for (;;)
107     {
108         DWORD total_size, len;
109         char temp[256];
110         KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)temp;
111
112         if (NtEnumerateValueKey( hkey, idx, KeyValueFullInformation,
113                                  temp, sizeof(temp), &total_size )) break;
114         if (info->Type != REG_SZ) break;
115
116         RtlUnicodeToMultiByteN( name, sizeof(name)-1, &len, info->Name, info->NameLength );
117         name[len] = 0;
118         RtlUnicodeToMultiByteN( buffer, sizeof(buffer)-1, &len,
119                                 (WCHAR *)(temp + info->DataOffset), total_size-info->DataOffset );
120         buffer[len] = 0;
121
122         idx++;
123         if(nports >4)
124           {
125             FIXME("Make the PPDeviceList larger than 5 elements\n");
126             break;
127           }
128         TRACE("Device '%s' at virtual userbase '%s'\n", buffer,name);
129         timeout = strchr(buffer,',');
130         if (timeout)
131           *timeout++=0;
132         fd=open(buffer,O_RDWR);
133         lasterror=errno;
134         if (fd == -1)
135           {
136             WARN("Configuration: No access to %s Cause: %s\n",buffer,strerror(lasterror));
137             WARN("Rejecting configuration item\n");
138             if (lasterror == ENODEV)
139               ERR("Is the ppdev module loaded?\n");
140             continue;
141           }
142         userbase = strtol(name,(char **)NULL, 16);
143         if ( errno == ERANGE)
144           {
145             WARN("Configuration: Invalid base %s for %s\n",name,buffer);
146             WARN("Rejecting configuration item\n");
147             continue;
148           }
149         if (ioctl (fd,PPCLAIM,0))
150           {
151             ERR("PPCLAIM rejected %s\n",buffer);
152             ERR("Perhaps the device is already in use or nonexistent\n");
153             continue;
154           }
155         if (nports > 0)
156           {
157             for (i=0; i<= nports; i++)
158               {
159                 if (PPDeviceList[i].userbase == userbase)
160                   {
161                     WARN("Configuration: %s uses the same virtual ports as %s\n",
162                          buffer,PPDeviceList[0].devicename);
163                     WARN("Configuration: Rejecting configuration item\n");
164                     userbase = 0;
165                     break;
166                   }
167               }
168             if (!userbase) continue;
169           }
170         /* Check for the minimum required IOCTLS */
171         if ((ioctl(fd,PPRDATA,&res))||
172             (ioctl(fd,PPRSTATUS,&res))||
173             (ioctl(fd,PPRCONTROL,&res)))
174           {
175             ERR("PPUSER IOCTL not available for parport device %s\n",buffer);
176             continue;
177           }
178         if (ioctl (fd,PPRELEASE,0))
179           {
180             ERR("PPRELEASE rejected %s\n",buffer);
181             ERR("Perhaps the device is already in use or nonexistent\n");
182             continue;
183           }
184         PPDeviceList[nports].devicename = malloc(sizeof(buffer)+1);
185         if (!PPDeviceList[nports].devicename)
186           {
187             ERR("No (more) space for devicename\n");
188             break;
189           }
190         strcpy(PPDeviceList[nports].devicename,buffer);
191         PPDeviceList[nports].fd = fd;
192         PPDeviceList[nports].userbase = userbase;
193         PPDeviceList[nports].lastaccess=GetTickCount();
194         if (timeout)
195           {
196             PPDeviceList[nports].timeout = strtol(timeout,(char **)NULL, 10);
197             if (errno == ERANGE)
198               {
199                 WARN("Configuration: Invalid timeout %s in configuration for %s, Setting to 0\n",
200                      timeout,buffer);
201                 PPDeviceList[nports].timeout = 0;
202               }
203           }
204         else
205           PPDeviceList[nports].timeout = 0;
206         nports++;
207     }
208     TRACE("found %d ports\n",nports);
209     NtClose( hkey );
210
211     PPDeviceNum= nports;
212     if (nports > 1)
213       /* sort in ascending order for userbase for faster access */
214       qsort (PPDeviceList,PPDeviceNum,sizeof(PPDeviceStruct),IO_pp_sort);
215
216     if (nports)
217       ret=0;
218     for (idx= 0;idx<PPDeviceNum; idx++)
219       TRACE("found device %s userbase %x fd %x timeout %d\n",
220             PPDeviceList[idx].devicename, PPDeviceList[idx].userbase,
221             PPDeviceList[idx].fd,PPDeviceList[idx].timeout);
222     /* FIXME:
223        register a timer callback perhaps every 30 seconds to release unused ports
224        Set lastaccess = 0 as indicator when port was released
225     */
226     return ret;
227 }
228
229 /* IO_pp_do_access
230  *
231  * Do the actual IOCTL
232  * Return NULL on success
233  */
234 static int IO_pp_do_access(int idx,int ppctl, DWORD* res)
235 {
236   int ret;
237   if (ioctl(PPDeviceList[idx].fd,PPCLAIM,0))
238     {
239       ERR("Can't reclaim device %s, PPUSER/PPDEV handling confused\n",
240           PPDeviceList[idx].devicename);
241       return 1;
242     }
243   ret = ioctl(PPDeviceList[idx].fd,ppctl,res);
244   if (ioctl(PPDeviceList[idx].fd,PPRELEASE,0))
245     {
246       ERR("Can't release device %s, PPUSER/PPDEV handling confused\n",
247           PPDeviceList[idx].devicename);
248       return 1;
249     }
250   return ret;
251
252 }
253
254 /* IO_pp_inp
255  *
256  * Check if we can satisfy the INP command with some of the configured PPDEV deviced
257  * Return NULL on success
258  */
259 int IO_pp_inp(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,PPRDATA,res);
271          case 1:
272            return IO_pp_do_access(idx,PPRSTATUS,res);
273          case 2:
274            return IO_pp_do_access(idx,PPRCONTROL,res);
275          case 0x400:
276          case 0x402:
277          case 3:
278          case 4:
279          case 0x401:
280            FIXME("Port 0x%x not accessible for reading with ppdev\n",port);
281            FIXME("If this is causing problems, try direct port access\n");
282            return 1;
283          default:
284            break;
285          }
286       }
287     return 1;
288 }
289
290 /* IO_pp_outp
291  *
292  * Check if we can satisfy the OUTP command with some of the configured PPDEV deviced
293  * Return NULL on success
294  */
295 BOOL IO_pp_outp(int port, DWORD* res)
296 {
297     int idx,j=0;
298
299     for (idx=0;idx<PPDeviceNum ;idx++)
300       {
301        j = port - PPDeviceList[idx].userbase;
302        if (j <0) return 1;
303        switch (j)
304          {
305          case 0:
306            return IO_pp_do_access(idx,PPWDATA,res);
307          case 2:
308            { 
309              /* We can't switch port direction via PPWCONTROL,
310                 so do it via PPDATADIR
311              */
312              DWORD mode = *res & 0x20;
313              IO_pp_do_access(idx,PPDATADIR,&mode);
314              mode = (*res & ~0x20);
315              return IO_pp_do_access(idx,PPWCONTROL,&mode);
316            }
317
318          case 1:
319          case 0x400:
320          case 0x402:
321          case 3:
322          case 4:
323          case 0x401:
324            FIXME("Port %d not accessible for writing with ppdev\n",port);
325            FIXME("If this is causing problems, try direct port access\n");
326            return 1;
327          default:
328            break;
329          }
330       }
331     return TRUE;
332 }
333
334
335 #else /* HAVE_PPDEV */
336
337
338 char IO_pp_init(void)
339 {
340   return 1;
341 }
342
343 int IO_pp_inp(int port, DWORD* res)
344 {
345   return 1;
346 }
347
348 BOOL IO_pp_outp(int port, DWORD* res)
349 {
350   return TRUE;
351 }
352 #endif  /* HAVE_PPDEV */