server: Add support for generic device objects managed from the client side.
diff --git a/server/Makefile.in b/server/Makefile.in
index 5e31826..7648281 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -19,6 +19,7 @@
 	context_sparc.c \
 	context_x86_64.c \
 	debugger.c \
+	device.c \
 	directory.c \
 	event.c \
 	fd.c \
diff --git a/server/device.c b/server/device.c
new file mode 100644
index 0000000..8a911b7
--- /dev/null
+++ b/server/device.c
@@ -0,0 +1,259 @@
+/*
+ * Server-side device support
+ *
+ * Copyright (C) 2007 Alexandre Julliard
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winternl.h"
+
+#include "object.h"
+#include "file.h"
+#include "handle.h"
+#include "request.h"
+
+struct device_manager
+{
+    struct object          obj;           /* object header */
+    struct list            devices;       /* list of devices */
+};
+
+static void device_manager_dump( struct object *obj, int verbose );
+static void device_manager_destroy( struct object *obj );
+
+static const struct object_ops device_manager_ops =
+{
+    sizeof(struct device_manager),    /* size */
+    device_manager_dump,              /* dump */
+    no_add_queue,                     /* add_queue */
+    NULL,                             /* remove_queue */
+    NULL,                             /* signaled */
+    no_satisfied,                     /* satisfied */
+    no_signal,                        /* signal */
+    no_get_fd,                        /* get_fd */
+    no_map_access,                    /* map_access */
+    no_lookup_name,                   /* lookup_name */
+    no_open_file,                     /* open_file */
+    no_close_handle,                  /* close_handle */
+    device_manager_destroy            /* destroy */
+};
+
+
+struct device
+{
+    struct object          obj;           /* object header */
+    struct device_manager *manager;       /* manager for this device (or NULL if deleted) */
+    struct fd             *fd;            /* file descriptor for ioctl */
+    void                  *user_ptr;      /* opaque ptr for client side */
+    struct list            entry;         /* entry in device manager list */
+};
+
+static void device_dump( struct object *obj, int verbose );
+static struct fd *device_get_fd( struct object *obj );
+static void device_destroy( struct object *obj );
+static struct object *device_open_file( struct object *obj, unsigned int access,
+                                        unsigned int sharing, unsigned int options );
+static enum server_fd_type device_get_fd_type( struct fd *fd );
+
+static const struct object_ops device_ops =
+{
+    sizeof(struct device),            /* size */
+    device_dump,                      /* dump */
+    no_add_queue,                     /* add_queue */
+    NULL,                             /* remove_queue */
+    NULL,                             /* signaled */
+    no_satisfied,                     /* satisfied */
+    no_signal,                        /* signal */
+    device_get_fd,                    /* get_fd */
+    no_map_access,                    /* map_access */
+    no_lookup_name,                   /* lookup_name */
+    device_open_file,                 /* open_file */
+    no_close_handle,                  /* close_handle */
+    device_destroy                    /* destroy */
+};
+
+static const struct fd_ops device_fd_ops =
+{
+    default_fd_get_poll_events,       /* get_poll_events */
+    default_poll_event,               /* poll_event */
+    no_flush,                         /* flush */
+    device_get_fd_type,               /* get_fd_type */
+    default_fd_ioctl,                 /* ioctl */
+    default_fd_queue_async,           /* queue_async */
+    default_fd_reselect_async,        /* reselect_async */
+    default_fd_cancel_async           /* cancel_async */
+};
+
+
+static void device_dump( struct object *obj, int verbose )
+{
+    struct device *device = (struct device *)obj;
+
+    fprintf( stderr, "Device " );
+    dump_object_name( &device->obj );
+    fputc( '\n', stderr );
+}
+
+static struct fd *device_get_fd( struct object *obj )
+{
+    struct device *device = (struct device *)obj;
+
+    return (struct fd *)grab_object( device->fd );
+}
+
+static void device_destroy( struct object *obj )
+{
+    struct device *device = (struct device *)obj;
+
+    if (device->fd) release_object( device->fd );
+    if (device->manager) list_remove( &device->entry );
+}
+
+static struct object *device_open_file( struct object *obj, unsigned int access,
+                                        unsigned int sharing, unsigned int options )
+{
+    return grab_object( obj );
+}
+
+static enum server_fd_type device_get_fd_type( struct fd *fd )
+{
+    return FD_TYPE_DEVICE;
+}
+
+static struct device *create_device( struct directory *root, const struct unicode_str *name,
+                                     struct device_manager *manager, unsigned int attr )
+{
+    struct device *device;
+
+    if ((device = create_named_object_dir( root, name, attr, &device_ops )))
+    {
+        if (get_error() != STATUS_OBJECT_NAME_EXISTS)
+        {
+            /* initialize it if it didn't already exist */
+            device->manager = manager;
+            list_add_tail( &manager->devices, &device->entry );
+            if (!(device->fd = alloc_pseudo_fd( &device_fd_ops, &device->obj, 0 )))
+            {
+                release_object( device );
+                device = NULL;
+            }
+        }
+    }
+    return device;
+}
+
+static void delete_device( struct device *device )
+{
+    if (!device->manager) return;  /* already deleted */
+
+    unlink_named_object( &device->obj );
+    list_remove( &device->entry );
+    device->manager = NULL;
+}
+
+
+static void device_manager_dump( struct object *obj, int verbose )
+{
+    fprintf( stderr, "Device manager\n" );
+}
+
+static void device_manager_destroy( struct object *obj )
+{
+    struct device_manager *manager = (struct device_manager *)obj;
+    struct list *ptr;
+
+    while ((ptr = list_head( &manager->devices )))
+    {
+        struct device *device = LIST_ENTRY( ptr, struct device, entry );
+        delete_device( device );
+    }
+}
+
+static struct device_manager *create_device_manager(void)
+{
+    struct device_manager *manager;
+
+    if ((manager = alloc_object( &device_manager_ops )))
+    {
+        list_init( &manager->devices );
+    }
+    return manager;
+}
+
+
+/* create a device manager */
+DECL_HANDLER(create_device_manager)
+{
+    struct device_manager *manager = create_device_manager();
+
+    if (manager)
+    {
+        reply->handle = alloc_handle( current->process, manager, req->access, req->attributes );
+        release_object( manager );
+    }
+}
+
+
+/* create a device */
+DECL_HANDLER(create_device)
+{
+    struct device *device;
+    struct unicode_str name;
+    struct device_manager *manager;
+    struct directory *root = NULL;
+
+    if (!(manager = (struct device_manager *)get_handle_obj( current->process, req->manager,
+                                                             0, &device_manager_ops )))
+        return;
+
+    get_req_unicode_str( &name );
+    if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
+    {
+        release_object( manager );
+        return;
+    }
+
+    if ((device = create_device( root, &name, manager, req->attributes )))
+    {
+        device->user_ptr = req->user_ptr;
+        reply->handle = alloc_handle( current->process, device, req->access, req->attributes );
+        release_object( device );
+    }
+
+    if (root) release_object( root );
+    release_object( manager );
+}
+
+
+/* delete a device */
+DECL_HANDLER(delete_device)
+{
+    struct device *device;
+
+    if ((device = (struct device *)get_handle_obj( current->process, req->handle, 0, &device_ops )))
+    {
+        delete_device( device );
+        release_object( device );
+    }
+}
diff --git a/server/protocol.def b/server/protocol.def
index dd59acb..a7b4dfd 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2854,3 +2854,31 @@
 @REPLY
     luid_t         luid;
 @END
+
+
+/* Create a device manager */
+@REQ(create_device_manager)
+    unsigned int access;          /* wanted access rights */
+    unsigned int attributes;      /* object attributes */
+@REPLY
+    obj_handle_t handle;          /* handle to the device */
+@END
+
+
+/* Create a device */
+@REQ(create_device)
+    unsigned int access;          /* wanted access rights */
+    unsigned int attributes;      /* object attributes */
+    obj_handle_t rootdir;         /* root directory */
+    obj_handle_t manager;         /* device manager */
+    void*        user_ptr;        /* opaque ptr for use by client */
+    VARARG(name,unicode_str);     /* object name */
+@REPLY
+    obj_handle_t handle;          /* handle to the device */
+@END
+
+
+/* Delete a device */
+@REQ(delete_device)
+    obj_handle_t handle;          /* handle to the device */
+@END
diff --git a/server/request.h b/server/request.h
index 3630154..6d769c1 100644
--- a/server/request.h
+++ b/server/request.h
@@ -324,6 +324,9 @@
 DECL_HANDLER(get_object_info);
 DECL_HANDLER(get_token_impersonation_level);
 DECL_HANDLER(allocate_locally_unique_id);
+DECL_HANDLER(create_device_manager);
+DECL_HANDLER(create_device);
+DECL_HANDLER(delete_device);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -544,6 +547,9 @@
     (req_handler)req_get_object_info,
     (req_handler)req_get_token_impersonation_level,
     (req_handler)req_allocate_locally_unique_id,
+    (req_handler)req_create_device_manager,
+    (req_handler)req_create_device,
+    (req_handler)req_delete_device,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/trace.c b/server/trace.c
index 71e5cb0..5762331 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3460,6 +3460,38 @@
     dump_luid( &req->luid );
 }
 
+static void dump_create_device_manager_request( const struct create_device_manager_request *req )
+{
+    fprintf( stderr, " access=%08x,", req->access );
+    fprintf( stderr, " attributes=%08x", req->attributes );
+}
+
+static void dump_create_device_manager_reply( const struct create_device_manager_reply *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_create_device_request( const struct create_device_request *req )
+{
+    fprintf( stderr, " access=%08x,", req->access );
+    fprintf( stderr, " attributes=%08x,", req->attributes );
+    fprintf( stderr, " rootdir=%p,", req->rootdir );
+    fprintf( stderr, " manager=%p,", req->manager );
+    fprintf( stderr, " user_ptr=%p,", req->user_ptr );
+    fprintf( stderr, " name=" );
+    dump_varargs_unicode_str( cur_size );
+}
+
+static void dump_create_device_reply( const struct create_device_reply *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_delete_device_request( const struct delete_device_request *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_get_new_process_info_request,
@@ -3675,6 +3707,9 @@
     (dump_func)dump_get_object_info_request,
     (dump_func)dump_get_token_impersonation_level_request,
     (dump_func)dump_allocate_locally_unique_id_request,
+    (dump_func)dump_create_device_manager_request,
+    (dump_func)dump_create_device_request,
+    (dump_func)dump_delete_device_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -3892,6 +3927,9 @@
     (dump_func)dump_get_object_info_reply,
     (dump_func)dump_get_token_impersonation_level_reply,
     (dump_func)dump_allocate_locally_unique_id_reply,
+    (dump_func)dump_create_device_manager_reply,
+    (dump_func)dump_create_device_reply,
+    (dump_func)0,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -4109,6 +4147,9 @@
     "get_object_info",
     "get_token_impersonation_level",
     "allocate_locally_unique_id",
+    "create_device_manager",
+    "create_device",
+    "delete_device",
 };
 
 static const struct