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