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