This patch provides initial support for accessing LPT0 via /dev/lp0. It provides this support in very much the same way serial port support is provided - most of the functions are subtly altered duplicates of that code. Currently only syncronous accesses are supported, although the infra-structure for asyncronous methods is in place it has not been tested. It would be a good starting point for anyone to get this working at a later date though. The patch enables programs that communicate with parallel port devices directly through LPT0 to work. Ed Lea <ed@orangebits.cx>
? server/parallel.c Index: files/dos_fs.c =================================================================== RCS file: /home/wine/wine/files/dos_fs.c,v retrieving revision 1.135 diff -u -u -r1.135 dos_fs.c --- files/dos_fs.c 2 Sep 2003 18:14:50 -0000 1.135 +++ files/dos_fs.c 4 Sep 2003 14:56:47 -0000 @@ -930,6 +930,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 * @@ -957,6 +1018,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, @@ -988,6 +1050,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.81 diff -u -u -r1.81 server_protocol.h --- include/wine/server_protocol.h 28 Aug 2003 21:43:34 -0000 1.81 +++ include/wine/server_protocol.h 4 Sep 2003 14:56:47 -0000 @@ -2393,6 +2393,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; @@ -3246,6 +3264,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, @@ -3431,6 +3450,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; @@ -3614,6 +3634,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 4 Sep 2003 14:56:47 -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.80 diff -u -u -r1.80 protocol.def --- server/protocol.def 19 Aug 2003 03:08:17 -0000 1.80 +++ server/protocol.def 4 Sep 2003 14:56:47 -0000 @@ -1698,6 +1698,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 4 Sep 2003 14:56:47 -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-04 16:02:19.000000000 +0100 @@ -0,0 +1,327 @@ +/* + * 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 "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 ); + } +}