This patch provides initial support for access to a parallel port (LPTx) through the DOSFS_OpenDevice mechanism. The code utilises the current default entry in the [parallelports] section of ~/.wine/config, i.e. [parallelports] "Lpt1" = "/dev/lp0" The code compliments the existing serial port support, however has been kept seperate from that for conceptual and technical reasons. Some breakages have been fixed that resulted from changes introduced to Wine since the first time I sent the patch. Changelog - files/dos_fs.c Added DOSFS_CreateParallelPort, called from DOSFS_OpenDevice when a connection to LPTx is required - include/wine/server_protocol.h, server/protocol.def Wine Server protocol details for serving parallel port requests - server/parallel.c Handles all asyncronous lp communication. Has initial framework for syncronous communication, currently unused. - server/Makefile.in Specifies that parallel.o should be built Ed Lea <ed@jellybaby.net> -- Ed. If you can read this, you're too close.
Index: files/dos_fs.c =================================================================== RCS file: /home/wine/wine/files/dos_fs.c,v retrieving revision 1.139 diff -u -u -r1.139 dos_fs.c --- files/dos_fs.c 18 Sep 2003 04:25:31 -0000 1.139 +++ files/dos_fs.c 23 Sep 2003 12:38:00 -0000 @@ -934,6 +934,67 @@ return ret; } + +/************************************************************************** + * DOSFS_CreateParallelPort + */ +static HANDLE DOSFS_CreateParallelPort(LPCWSTR name, DWORD access, DWORD attributes, LPSECURITY_ATTRIBUTES sa) +{ + HANDLE ret; + HKEY hkey; + DWORD dummy; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + WCHAR *devnameW; + char tmp[128]; + char devname[40]; + + static const WCHAR parallelportsW[] = {'M','a','c','h','i','n','e','\\', + 'S','o','f','t','w','a','r','e','\\', + 'W','i','n','e','\\','W','i','n','e','\\', + 'C','o','n','f','i','g','\\', + 'P','a','r','a','l','l','e','l','P','o','r','t','s',0}; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, parallelportsW ); + + if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return 0; + + RtlInitUnicodeString( &nameW, name ); + if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy )) + devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; + else + devnameW = NULL; + + NtClose( hkey ); + + if (!devnameW) return 0; + WideCharToMultiByte(CP_ACP, 0, devnameW, -1, devname, sizeof(devname), NULL, NULL); + + SERVER_START_REQ( create_parallel ) + { + req->access = access; + req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle); + req->attributes = attributes; + req->sharing = FILE_SHARE_READ|FILE_SHARE_WRITE; + wine_server_add_data( req, devname, strlen(devname) ); + SetLastError(0); + wine_server_call_err( req ); + ret = reply->handle; + } + SERVER_END_REQ; + + if (!ret) + ERR("Could not open device '%s'.\n", devname); + return ret; +} + + /*********************************************************************** * DOSFS_OpenDevice * @@ -961,6 +1022,7 @@ static const WCHAR scsimgrW[] = {'S','C','S','I','M','G','R','$',0}; static const WCHAR hpscanW[] = {'H','P','S','C','A','N',0}; static const WCHAR emmxxxx0W[] = {'E','M','M','X','X','X','X','0',0}; + static const WCHAR lpt1[] = {'L', 'P', 'T', '1', 0}; /* got it */ if (!strcmpiW(DOSFS_Devices[i].name, nulW)) return FILE_CreateFile( "/dev/null", access, @@ -992,6 +1054,11 @@ { return FILE_CreateDevice( i, access, sa ); } + if (!strcmpiW(DOSFS_Devices[i].name, lpt1)) + { + if (handle = DOSFS_CreateParallelPort(DOSFS_Devices[i].name,access,attributes,sa)) + return handle; + } if( (handle=DOSFS_CreateCommPort(DOSFS_Devices[i].name,access,attributes,sa)) ) return handle; Index: include/wine/server_protocol.h =================================================================== RCS file: /home/wine/wine/include/wine/server_protocol.h,v retrieving revision 1.83 diff -u -u -r1.83 server_protocol.h --- include/wine/server_protocol.h 16 Sep 2003 01:07:22 -0000 1.83 +++ include/wine/server_protocol.h 23 Sep 2003 12:38:00 -0000 @@ -2397,6 +2397,24 @@ +struct create_parallel_request +{ + struct request_header __header; + unsigned int access; + int inherit; + unsigned int attributes; + unsigned int sharing; + /* VARARG(name,string); */ +}; +struct create_parallel_reply +{ + struct reply_header __header; + obj_handle_t handle; +}; + + + + struct register_async_request { struct request_header __header; @@ -3250,6 +3268,7 @@ REQ_create_serial, REQ_get_serial_info, REQ_set_serial_info, + REQ_create_parallel, REQ_register_async, REQ_create_named_pipe, REQ_open_named_pipe, @@ -3435,6 +3454,7 @@ struct create_serial_request create_serial_request; struct get_serial_info_request get_serial_info_request; struct set_serial_info_request set_serial_info_request; + struct create_parallel_request create_parallel_request; struct register_async_request register_async_request; struct create_named_pipe_request create_named_pipe_request; struct open_named_pipe_request open_named_pipe_request; @@ -3618,6 +3638,7 @@ struct create_serial_reply create_serial_reply; struct get_serial_info_reply get_serial_info_reply; struct set_serial_info_reply set_serial_info_reply; + struct create_parallel_reply create_parallel_reply; struct register_async_reply register_async_reply; struct create_named_pipe_reply create_named_pipe_reply; struct open_named_pipe_reply open_named_pipe_reply; Index: server/Makefile.in =================================================================== RCS file: /home/wine/wine/server/Makefile.in,v retrieving revision 1.47 diff -u -u -r1.47 Makefile.in --- server/Makefile.in 24 Jul 2003 00:07:00 -0000 1.47 +++ server/Makefile.in 23 Sep 2003 12:38:00 -0000 @@ -26,6 +26,7 @@ mutex.c \ named_pipe.c \ object.c \ + parallel.c \ process.c \ ptrace.c \ queue.c \ Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.82 diff -u -u -r1.82 protocol.def --- server/protocol.def 16 Sep 2003 01:07:21 -0000 1.82 +++ server/protocol.def 23 Sep 2003 12:38:00 -0000 @@ -1702,6 +1702,18 @@ #define SERIALINFO_SET_ERROR 0x04 +/* Open a parallel port */ +@REQ(create_parallel) + unsigned int access; /* wanted access rights */ + int inherit; /* inherit flag */ + unsigned int attributes; /* eg. FILE_FLAG_OVERLAPPED */ + unsigned int sharing; /* sharing flags */ + VARARG(name,string); /* file name */ +@REPLY + obj_handle_t handle; /* handle to the port */ +@END + + /* Create / reschedule an async I/O */ @REQ(register_async) obj_handle_t handle; /* handle to comm port, socket or file */ Index: server/request.h =================================================================== RCS file: /home/wine/wine/server/request.h,v retrieving revision 1.88 diff -u -u -r1.88 request.h --- server/request.h 19 Aug 2003 03:08:17 -0000 1.88 +++ server/request.h 23 Sep 2003 12:38:00 -0000 @@ -236,6 +236,7 @@ DECL_HANDLER(create_serial); DECL_HANDLER(get_serial_info); DECL_HANDLER(set_serial_info); +DECL_HANDLER(create_parallel); DECL_HANDLER(register_async); DECL_HANDLER(create_named_pipe); DECL_HANDLER(open_named_pipe); @@ -420,6 +421,7 @@ (req_handler)req_create_serial, (req_handler)req_get_serial_info, (req_handler)req_set_serial_info, + (req_handler)req_create_parallel, (req_handler)req_register_async, (req_handler)req_create_named_pipe, (req_handler)req_open_named_pipe, --- /dev/null 2003-03-14 13:07:09.000000000 +0000 +++ server/parallel.c 2003-09-08 12:29:17.000000000 +0100 @@ -0,0 +1,328 @@ +/* + * Server-side parallel port communications management + * + * Copyright (C) 1998 Alexandre Julliard + * Copyright (C) 2000,2001 Mike McCormack + * Copyright (C) 2003 Ed Lea + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This file is basically serial.c adapted for parallel accesses. + * FIXME: all accesses are done syncronously even if the access method specifies + * that they should be done asyncronously. The framework for asyncronous + * requests has been implemented from serial.c but remains untested. + */ + +#include "config.h" +#include "wine/port.h" + +#include <assert.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include "winerror.h" +#include "windef.h" +#include "winbase.h" + +#include "file.h" +#include "handle.h" +#include "thread.h" +#include "request.h" +#include "async.h" + +static struct fd *parallel_get_fd( struct object *obj ); +static void parallel_destroy( struct object *obj); +static void parallel_dump( struct object *obj, int verbose ); + +static int parallel_get_poll_events( struct fd *fd ); +static void parallel_poll_event( struct fd *fd, int event ); +static int parallel_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags ); +static int parallel_flush( struct fd *fd, struct event **event ); +static void parallel_queue_async(struct fd *fd, void *ptr, unsigned int status, int type, int count); + +struct parallel +{ + struct object obj; + struct fd *fd; + unsigned int access; + unsigned int attrib; + + /* timeout values */ + unsigned int readinterval; + unsigned int readconst; + unsigned int readmult; + unsigned int writeconst; + unsigned int writemult; + + struct async_queue read_q; + struct async_queue write_q; + struct async_queue wait_q; + +}; + +static const struct object_ops parallel_ops = +{ + sizeof(struct parallel), /* size */ + parallel_dump, /* dump */ + default_fd_add_queue, /* add_queue */ + default_fd_remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + no_satisfied, /* satisfied */ + parallel_get_fd, /* get_fd */ + parallel_destroy /* destroy */ +}; + +static const struct fd_ops parallel_fd_ops = +{ + parallel_get_poll_events, /* get_poll_events */ + parallel_poll_event, /* poll_event */ + parallel_flush, /* flush */ + parallel_get_info, /* get_file_info */ + parallel_queue_async /* queue_async */ +}; + +static struct parallel *create_parallel( const char *nameptr, size_t len, unsigned int access, int attributes ) +{ + struct parallel *parallel; + int fd, flags = 0; + char *name; + + if (!(name = mem_alloc( len + 1 ))) return NULL; + memcpy( name, nameptr, len ); + name[len] = 0; + + switch(access & (GENERIC_READ | GENERIC_WRITE)) + { + case GENERIC_READ: flags |= O_RDONLY; break; + case GENERIC_WRITE: flags |= O_WRONLY; break; + case GENERIC_READ|GENERIC_WRITE: flags |= O_RDWR; break; + default: break; + } + + flags |= O_NONBLOCK; + + fd = open( name, flags ); + free( name ); + if (fd < 0) + { + file_set_error(); + return NULL; + } + + /* set the fd back to blocking if necessary */ + if( ! (attributes & FILE_FLAG_OVERLAPPED) ) + if(0>fcntl(fd, F_SETFL, 0)) + perror("fcntl"); + + if (!(parallel = alloc_object( ¶llel_ops ))) + { + close( fd ); + return NULL; + } + parallel->attrib = attributes; + parallel->access = access; + parallel->readinterval = 0; + parallel->readmult = 0; + parallel->readconst = 0; + parallel->writemult = 0; + parallel->writeconst = 0; + init_async_queue(¶llel->read_q); + init_async_queue(¶llel->write_q); + init_async_queue(¶llel->wait_q); + if (!(parallel->fd = create_anonymous_fd( ¶llel_fd_ops, fd, ¶llel->obj ))) + { + release_object( parallel ); + return NULL; + } + return parallel; +} + +static struct fd *parallel_get_fd( struct object *obj ) +{ + struct parallel *parallel = (struct parallel *)obj; + return (struct fd *)grab_object( parallel->fd ); +} + +static void parallel_destroy( struct object *obj) +{ + struct parallel *parallel = (struct parallel *)obj; + + destroy_async_queue(¶llel->read_q); + destroy_async_queue(¶llel->write_q); + destroy_async_queue(¶llel->wait_q); + if (parallel->fd) release_object( parallel->fd ); +} + +static void parallel_dump( struct object *obj, int verbose ) +{ + struct parallel *parallel = (struct parallel *)obj; + assert( obj->ops == ¶llel_ops ); + fprintf( stderr, "Port fd=%p\n", parallel->fd ); +} + +static struct parallel *get_parallel_obj( struct process *process, obj_handle_t handle, unsigned int access ) +{ + return (struct parallel *)get_handle_obj( process, handle, access, ¶llel_ops ); +} + +static int parallel_get_poll_events (struct fd *fd) +{ + struct parallel *parallel = get_fd_user(fd); + int events = 0; + assert(parallel->obj.ops == ¶llel_ops); + + if (IS_READY(parallel->read_q)) + events |= POLLIN; + if (IS_READY(parallel->write_q)) + events |= POLLOUT; + if (IS_READY(parallel->wait_q)) + events |= POLLIN; + + return events; +} + +static int parallel_get_info (struct fd *fd, struct get_file_info_reply *reply, int *flags) +{ + struct parallel *parallel = get_fd_user(fd); + assert(parallel->obj.ops == ¶llel_ops); + + if (reply) + { + reply->type = FILE_TYPE_CHAR; + reply->attr = 0; + reply->access_time = 0; + reply->write_time = 0; + reply->size_high = 0; + reply->size_low = 0; + reply->links = 0; + reply->index_high = 0; + reply->index_low = 0; + reply->serial = 0; + } + + /* Only synchronous writes are currently supported. If FILE_FLAG_OVERLAPPED + * or FD_FLAG_TIMEOUT are set NtWriteFile will attempt an asynchronous + * write which will not work */ + *flags = 0; + + return FD_TYPE_DEFAULT; +} + +static void parallel_poll_event (struct fd *fd, int event) +{ + struct parallel *parallel = get_fd_user( fd ); + + if (IS_READY(parallel->read_q) && (POLLIN & event)) + async_notify(parallel->read_q.head,STATUS_ALERTED); + + if (IS_READY(parallel->write_q) && (POLLOUT & event)) + async_notify(parallel->write_q.head,STATUS_ALERTED); + + if (IS_READY(parallel->wait_q) && (POLLIN & event)) + async_notify(parallel->wait_q.head,STATUS_ALERTED); + + set_fd_events(fd, parallel_get_poll_events(fd)); +} + +static void parallel_queue_async (struct fd *fd, void *ptr, unsigned int status, int type, int count) +{ + struct parallel *parallel = get_fd_user(fd); + struct async_queue *q; + struct async *async; + int timeout; + assert(parallel->obj.ops == ¶llel_ops); + + fprintf(stderr, "parallel_queue_async\n"); + switch(type) + { + case ASYNC_TYPE_READ: + q = ¶llel->read_q; + timeout = parallel->readconst + parallel->readmult*count; + break; + case ASYNC_TYPE_WAIT: + q = ¶llel->wait_q; + timeout = 0; + break; + case ASYNC_TYPE_WRITE: + q = ¶llel->write_q; + timeout = parallel->writeconst + parallel->writemult*count; + break; + default: + set_error(STATUS_INVALID_PARAMETER); + return; + } + + async = find_async(q, current, ptr); + + if (status == STATUS_PENDING) + { + int events; + + if (!async) + async = create_async(¶llel->obj, current, ptr); + if (!async) + return; + + async->status = STATUS_PENDING; + if (!async->q) + { + async_add_timeout(async, timeout); + async_insert(q, async); + } + + events = check_fd_events(fd, parallel_get_poll_events(fd)); + if (events) + { + parallel_poll_event(fd, events); + return; + } + } + else if (async) destroy_async(async); + else set_error(STATUS_INVALID_PARAMETER); + + set_fd_events(fd, parallel_get_poll_events(fd)); +} + +static int parallel_flush (struct fd *fd, struct event **event) +{ + return 0; +} + +/* create a parallel */ +DECL_HANDLER(create_parallel) +{ + struct parallel *parallel; + + reply->handle = 0; + if ((parallel = create_parallel( get_req_data(), get_req_data_size(), req->access, req->attributes ))) + { + reply->handle = alloc_handle( current->process, parallel, req->access, req->inherit ); + + release_object( parallel ); + } +}